feat: 增加后端API接口
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-5020420260813003",
|
||||
"@wot-ui/ui": "^2.3.2",
|
||||
"miniprogram-api-typings": "^5.2.3",
|
||||
"pinia": "^4.0.3",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.1.9"
|
||||
},
|
||||
|
||||
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
@@ -62,6 +62,9 @@ importers:
|
||||
miniprogram-api-typings:
|
||||
specifier: ^5.2.3
|
||||
version: 5.2.3
|
||||
pinia:
|
||||
specifier: ^4.0.3
|
||||
version: 4.0.3(@vue/devtools-api@6.6.4)(typescript@4.9.5)(vue@3.5.42(typescript@4.9.5))
|
||||
vue:
|
||||
specifier: ^3.4.21
|
||||
version: 3.5.42(typescript@4.9.5)
|
||||
@@ -3578,6 +3581,16 @@ packages:
|
||||
resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pinia@4.0.3:
|
||||
resolution: {integrity: sha512-XMQqpvjgG7LMqVhhFUzKLT4KEbsYbfZZ0CZU9PgdFm1O2VKBmIkykL8+SfNhOaT7sR0wT6BEgKT0YNDYWgmVRA==}
|
||||
peerDependencies:
|
||||
'@vue/devtools-api': ^8.1.5
|
||||
typescript: '>=5.6.0'
|
||||
vue: ^3.5.11
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
pirates@4.0.7:
|
||||
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -9039,6 +9052,14 @@ snapshots:
|
||||
|
||||
picomatch@4.0.7: {}
|
||||
|
||||
pinia@4.0.3(@vue/devtools-api@6.6.4)(typescript@4.9.5)(vue@3.5.42(typescript@4.9.5)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
nostics: 1.2.0
|
||||
vue: 3.5.42(typescript@4.9.5)
|
||||
optionalDependencies:
|
||||
typescript: 4.9.5
|
||||
|
||||
pirates@4.0.7: {}
|
||||
|
||||
pixelmatch@4.0.2:
|
||||
|
||||
24
project.config.json
Normal file
24
project.config.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"setting": {
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"uglifyFileName": false,
|
||||
"enhance": true,
|
||||
"packNpmRelationList": [],
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
},
|
||||
"useCompilerPlugins": false
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"simulatorPluginLibVersion": {},
|
||||
"packOptions": {
|
||||
"ignore": [],
|
||||
"include": []
|
||||
},
|
||||
"appid": "wxf7a95cb4373170c1",
|
||||
"editorSetting": {}
|
||||
}
|
||||
14
project.private.config.json
Normal file
14
project.private.config.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"libVersion": "3.17.2",
|
||||
"projectname": "patch-mp",
|
||||
"setting": {
|
||||
"urlCheck": true,
|
||||
"coverView": true,
|
||||
"lazyloadPlaceholderEnable": false,
|
||||
"skylineRenderEnable": false,
|
||||
"preloadBackgroundData": false,
|
||||
"autoAudits": false,
|
||||
"showShadowRootInWxmlPanel": true,
|
||||
"compileHotReLoad": true
|
||||
}
|
||||
}
|
||||
124
src/components/CropPopup.vue
Normal file
124
src/components/CropPopup.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="patchStore.showCropPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
{{ patchStore.cropApiType === 'ADD' ? '新增作物' : '编辑作物' }}
|
||||
</view>
|
||||
|
||||
<wd-form
|
||||
ref="form"
|
||||
:model="patchStore.cropForm"
|
||||
layout="horizontal"
|
||||
title-width="80"
|
||||
size="large"
|
||||
border
|
||||
>
|
||||
<wd-form-item title="作物名称">
|
||||
<wd-input
|
||||
v-model="patchStore.cropForm.name"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入作物名称"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="作物种类">
|
||||
<wd-input
|
||||
v-model="patchStore.cropForm.variety"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入作物种类"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item
|
||||
title="种植日期"
|
||||
prop="date"
|
||||
is-link
|
||||
:value="patchStore.cropForm.date"
|
||||
placeholder="请选择种植日期"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
</wd-form>
|
||||
|
||||
<view class="flex w-full items-center justify-evenly">
|
||||
<wd-button type="info" @click="cancelClick">
|
||||
取消
|
||||
</wd-button>
|
||||
<wd-button v-if="patchStore.cropApiType === 'UPDATE'" type="danger" @click="deleteClick">
|
||||
删除
|
||||
</wd-button>
|
||||
<wd-button type="primary" @click="saveClick">
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<wd-datetime-picker
|
||||
:model-value="new Date(patchStore.recordForm.date).getTime()"
|
||||
v-model:visible="showTimePicker"
|
||||
type="date"
|
||||
:max-date="Date.now()"
|
||||
placeholder="请选择记录时间"
|
||||
@confirm="onDateConfirm"
|
||||
/>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { usePatchStore } from "@/stores/patch";
|
||||
import { formatDate } from "@/utils";
|
||||
|
||||
const patchStore = usePatchStore()
|
||||
const showTimePicker = ref(false)
|
||||
|
||||
|
||||
function onDateConfirm({ value }){
|
||||
patchStore.recordForm.date = formatDate(new Date(value))
|
||||
showTimePicker.value = false
|
||||
}
|
||||
|
||||
function cancelClick() {
|
||||
patchStore.showRecordPopup = false
|
||||
}
|
||||
|
||||
async function deleteClick() {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: `确定删除 ${patchStore.cropForm.name} 作物吗?`,
|
||||
async success(res){
|
||||
if (res.confirm) {
|
||||
await patchStore.deleteCropApi(patchStore.cropForm.id)
|
||||
await patchStore.queryPatch(patchStore.currentPatch.id)
|
||||
patchStore.recordList = []
|
||||
uni.showToast({ title: '删除作物成功', icon: 'none' })
|
||||
patchStore.showCropPopup = false
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function saveClick() {
|
||||
if (!patchStore.cropForm.name) {
|
||||
uni.showToast({ title: '请输入作物名称', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (patchStore.cropApiType === 'ADD') {
|
||||
await patchStore.addCropApi()
|
||||
await patchStore.queryPatch(patchStore.currentPatch.id)
|
||||
uni.showToast({ title: '新增作物成功', icon: 'none' })
|
||||
} else if (patchStore.cropApiType === 'UPDATE') {
|
||||
await patchStore.updateCropApi(patchStore.cropForm.id)
|
||||
await patchStore.queryPatch(patchStore.currentPatch.id)
|
||||
uni.showToast({ title: '更新作物成功', icon: 'none' })
|
||||
}
|
||||
|
||||
patchStore.showCropPopup = false
|
||||
}
|
||||
</script>
|
||||
106
src/components/PatchPopup.vue
Normal file
106
src/components/PatchPopup.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="patchStore.showPatchPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
{{ patchStore.patchApiType === 'ADD' ? '新增菜地' : '编辑菜地' }}
|
||||
</view>
|
||||
|
||||
<wd-form
|
||||
ref="form"
|
||||
:model="patchStore.patchForm"
|
||||
layout="horizontal"
|
||||
title-width="80"
|
||||
size="large"
|
||||
border
|
||||
>
|
||||
<wd-form-item title="名称">
|
||||
<wd-input
|
||||
v-model="patchStore.patchForm.name"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入菜地名称"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="地点">
|
||||
<wd-input
|
||||
v-model="patchStore.patchForm.location"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入菜地地点"
|
||||
/>
|
||||
</wd-form-item>
|
||||
</wd-form>
|
||||
|
||||
<view class="flex w-full items-center justify-evenly">
|
||||
<wd-button type="info" @click="cancelClick">
|
||||
取消
|
||||
</wd-button>
|
||||
<wd-button v-if="patchStore.patchApiType === 'UPDATE'" type="danger" @click="deleteClick">
|
||||
删除
|
||||
</wd-button>
|
||||
<wd-button type="primary" @click="saveClick">
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { usePatchStore } from "@/stores/patch";
|
||||
|
||||
const patchStore = usePatchStore()
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
patchStore.showPatchFab = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function cancelClick() {
|
||||
patchStore.showPatchPopup = false
|
||||
handeClosePopup()
|
||||
}
|
||||
|
||||
async function deleteClick() {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: `确定删除 ${patchStore.patchForm.name} 吗?`,
|
||||
async success(res){
|
||||
if (res.confirm) {
|
||||
await patchStore.deletePatchApi(patchStore.patchForm.id)
|
||||
await patchStore.queryPatchList()
|
||||
uni.showToast({ title: '删除菜地成功', icon: 'none' })
|
||||
patchStore.showPatchPopup = false
|
||||
handeClosePopup()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function saveClick() {
|
||||
if (!patchStore.patchForm.name) {
|
||||
uni.showToast({ title: '请输入菜地名称', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (patchStore.patchApiType === 'ADD') {
|
||||
await patchStore.addPatchApi()
|
||||
await patchStore.queryPatchList()
|
||||
uni.showToast({ title: '新增菜地成功', icon: 'none' })
|
||||
} else if (patchStore.patchApiType === 'UPDATE') {
|
||||
await patchStore.updatePatchApi(patchStore.patchForm.id)
|
||||
await patchStore.queryPatchList()
|
||||
uni.showToast({ title: '更新菜地成功', icon: 'none' })
|
||||
}
|
||||
|
||||
patchStore.showPatchPopup = false
|
||||
handeClosePopup()
|
||||
}
|
||||
</script>
|
||||
256
src/components/RecordPopup.vue
Normal file
256
src/components/RecordPopup.vue
Normal file
@@ -0,0 +1,256 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="patchStore.showRecordPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="flex flex-col max-h-[70vh] overflow-y-auto">
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
{{ patchStore.recordApiType === 'ADD' ? '新增记录' : '编辑记录' }}
|
||||
</view>
|
||||
|
||||
<wd-form
|
||||
ref="form"
|
||||
:model="patchStore.recordForm"
|
||||
layout="horizontal"
|
||||
title-width="80"
|
||||
size="large"
|
||||
border
|
||||
>
|
||||
<wd-form-item title="类型">
|
||||
<wd-radio-group v-model="patchStore.recordForm.type" type="button">
|
||||
<wd-radio
|
||||
v-for="item in typeColumns"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="内容">
|
||||
<wd-input
|
||||
v-model="patchStore.recordForm.content"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入菜地地点"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="时期">
|
||||
<wd-radio-group v-model="patchStore.recordForm.stage" type="button">
|
||||
<wd-radio
|
||||
v-for="item in stageColumns"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item
|
||||
title="日期"
|
||||
prop="date"
|
||||
is-link
|
||||
:value="patchStore.recordForm.date"
|
||||
placeholder="请选择记录日期"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
</wd-form>
|
||||
|
||||
<wd-form-item title="图片">
|
||||
<wd-upload v-model:file-list="patchStore.imageFileList" accept="image" multiple
|
||||
:limit="9" image-mode="aspectFill"
|
||||
:action="action" :success-status="[200, 201]"
|
||||
:before-upload="beforeImageUpload"
|
||||
:before-remove="beforeImageRemove"
|
||||
@success="handleImageSuccess"
|
||||
></wd-upload>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="视频">
|
||||
<wd-upload v-model:file-list="patchStore.videoFileList" accept="video" multiple
|
||||
:limit="3"
|
||||
:action="action" :success-status="[200, 201]"
|
||||
:before-upload="beforeVideoUpload"
|
||||
:before-remove="beforeVideoRemove"
|
||||
@success="handleVideoSuccess"></wd-upload>
|
||||
</wd-form-item>
|
||||
|
||||
<view class="flex w-full items-center justify-evenly">
|
||||
<wd-button type="info" @click="cancelClick">
|
||||
取消
|
||||
</wd-button>
|
||||
<wd-button v-if="patchStore.recordApiType === 'UPDATE'" type="danger" @click="deleteClick">
|
||||
删除
|
||||
</wd-button>
|
||||
<wd-button type="primary" @click="saveClick">
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<wd-datetime-picker
|
||||
:model-value="new Date(patchStore.recordForm.date).getTime()"
|
||||
v-model:visible="showTimePicker"
|
||||
type="date"
|
||||
:max-date="Date.now()"
|
||||
placeholder="请选择记录时间"
|
||||
@confirm="onDateConfirm"
|
||||
/>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { usePatchStore } from "@/stores/patch";
|
||||
import { formatDate } from "@/utils";
|
||||
import type { UploadBeforeUploadOption, UploadSuccessEvent, UploadRemoveEvent } from '@wot-ui/ui/components/wd-upload/types'
|
||||
|
||||
const patchStore = usePatchStore()
|
||||
const showTimePicker = ref(false)
|
||||
|
||||
const typeColumns = [
|
||||
{ label: '🌱 播种', value: '🌱 播种' },
|
||||
{ label: '💧 浇水', value: '💧 浇水' },
|
||||
{ label: '🌿 除草', value: '🌿 除草' },
|
||||
{ label: '🐛 除虫', value: '🐛 除虫' },
|
||||
{ label: '✂️ 修剪', value: '✂️ 修剪' },
|
||||
{ label: '🧪 施肥', value: '🧪 施肥' },
|
||||
{ label: '🔄 移栽', value: '🔄 移栽' },
|
||||
{ label: '🧺 收获', value: '🧺 收获' },
|
||||
]
|
||||
|
||||
const stageColumns = [
|
||||
{ label: '发芽期', value: '发芽期' },
|
||||
{ label: '幼苗期', value: '幼苗期' },
|
||||
{ label: '生长期', value: '生长期' },
|
||||
{ label: '开花期', value: '开花期' },
|
||||
{ label: '结果期', value: '结果期' },
|
||||
{ label: '采收期', value: '采收期' },
|
||||
]
|
||||
|
||||
const action = ref(`${patchStore.baseUrl}/patch-api/file/upload`)
|
||||
|
||||
function onDateConfirm({ value }){
|
||||
patchStore.recordForm.date = formatDate(new Date(value))
|
||||
showTimePicker.value = false
|
||||
}
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
patchStore.showRecordFab = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function cancelClick() {
|
||||
patchStore.showRecordPopup = false
|
||||
handeClosePopup()
|
||||
}
|
||||
|
||||
function beforeImageUpload(options: UploadBeforeUploadOption) {
|
||||
const file = options.files[0]
|
||||
const maxSize = 10 * 1024 * 1024
|
||||
|
||||
if (file.size > maxSize) {
|
||||
uni.showToast({
|
||||
title: '文件大小不能超过 10MB',
|
||||
icon: 'none',
|
||||
duration: 2500
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function handleImageSuccess(event: UploadSuccessEvent) {
|
||||
const url = JSON.parse(event.file.response)
|
||||
patchStore.recordForm.images.push(url)
|
||||
}
|
||||
|
||||
function beforeImageRemove(event: UploadRemoveEvent) {
|
||||
let url = ''
|
||||
if (patchStore.recordApiType === 'ADD') {
|
||||
url = JSON.parse(event.file.response)
|
||||
} else if (patchStore.recordApiType === 'UPDATE') {
|
||||
url = event.file.url.replace(`${patchStore.baseUrl}/`, '')
|
||||
}
|
||||
|
||||
if (url) {
|
||||
const index = patchStore.recordForm.images.indexOf(url)
|
||||
if (index > -1) {
|
||||
patchStore.recordForm.images.splice(index, 1)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function beforeVideoUpload(options: UploadBeforeUploadOption) {
|
||||
const file = options.files[0]
|
||||
const maxSize = 500 * 1024 * 1024
|
||||
|
||||
if (file.size > maxSize) {
|
||||
uni.showToast({
|
||||
title: '文件大小不能超过 500MB',
|
||||
icon: 'none',
|
||||
duration: 2500
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function handleVideoSuccess(event: UploadSuccessEvent) {
|
||||
const url = JSON.parse(event.file.response)
|
||||
patchStore.recordForm.videos.push(url)
|
||||
}
|
||||
|
||||
function beforeVideoRemove(event: UploadRemoveEvent) {
|
||||
const url = JSON.parse(event.file.response)
|
||||
if (url) {
|
||||
const index = patchStore.recordForm.videos.indexOf(url)
|
||||
if (index > -1) {
|
||||
patchStore.recordForm.videos.splice(index, 1)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function deleteClick() {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: `确定删除 ${patchStore.recordForm.type} 记录吗?`,
|
||||
async success(res){
|
||||
if (res.confirm) {
|
||||
await patchStore.deleteRecordApi(patchStore.recordForm.id)
|
||||
await patchStore.queryRecordList(patchStore.currentPatch.crop.id)
|
||||
uni.showToast({ title: '删除记录成功', icon: 'none' })
|
||||
patchStore.showRecordPopup = false
|
||||
handeClosePopup()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function saveClick() {
|
||||
if (patchStore.recordApiType === 'ADD') {
|
||||
await patchStore.addRecordApi()
|
||||
await patchStore.queryRecordList(patchStore.currentPatch.crop.id)
|
||||
uni.showToast({ title: '新增记录成功', icon: 'none' })
|
||||
} else if (patchStore.recordApiType === 'UPDATE') {
|
||||
await patchStore.updateRecordApi(patchStore.recordForm.id)
|
||||
await patchStore.queryRecordList(patchStore.currentPatch.crop.id)
|
||||
uni.showToast({ title: '更新记录成功', icon: 'none' })
|
||||
}
|
||||
|
||||
patchStore.showRecordPopup = false
|
||||
handeClosePopup()
|
||||
}
|
||||
</script>
|
||||
@@ -1,9 +1,13 @@
|
||||
import 'uno.css'
|
||||
import { createSSRApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
return { app }
|
||||
app.use(pinia)
|
||||
|
||||
return { app, pinia }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name" : "",
|
||||
"appid" : "",
|
||||
"appid" : "wxf7a95cb4373170c1",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : "100",
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
"path": "pages/index/index",
|
||||
"style": { "navigationBarTitleText": "首页" }
|
||||
},
|
||||
{
|
||||
"path": "pages/record/index",
|
||||
"style": { "navigationBarTitleText": "记录" }
|
||||
},
|
||||
{
|
||||
"path": "pages/user/index",
|
||||
"style": { "navigationBarTitleText": "我的" }
|
||||
|
||||
@@ -13,52 +13,51 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<wd-card title="1号菜地">
|
||||
<image src="https://wot-ui.cn/assets/panda.jpg" mode="aspectFill" />
|
||||
<wd-card v-for="(item, index) in patchStore.patchList" :key="item.id">
|
||||
<template #title>
|
||||
<view class="wd-card__title-text flex items-center justify-between">
|
||||
<text>{{ item.name }}</text>
|
||||
<text v-if="item.location" class="text-sm text-[#999]">{{ item.location }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<wd-img width="100%" height="40vh" :src="`${patchStore.baseUrl}/${item.crop?.images[0]}`"
|
||||
lazy-load mode="aspectFill" @click="handlePreview(item.crop)">
|
||||
<template #error>
|
||||
<view class="error-wrap">暂无记录</view>
|
||||
</template>
|
||||
</wd-img>
|
||||
|
||||
<template #footer>
|
||||
<view class="flex-1 flex justify-between items-center">
|
||||
<view class="flex flex-col gap-1">
|
||||
<view v-if="item.crop" class="flex flex-col gap-1">
|
||||
<view class="text-base font-medium">
|
||||
西红柿
|
||||
<text class="text-sm text-gray-400 font-normal ml-1">粉果番茄</text>
|
||||
{{ item.crop.name }}
|
||||
<text class="text-sm text-gray-400 font-normal ml-1">{{ item.crop.variety }}</text>
|
||||
</view>
|
||||
<view class="flex gap-1 align-center">
|
||||
<wd-text text="🌱 生长期"/>
|
||||
<wd-text text="📅 2025-04-10"/>
|
||||
<wd-text v-if="item.crop.stage" :text="item.crop.stage"/>
|
||||
<wd-text :text="`📅 ${item.crop.date}`"/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<wd-text v-else text="暂无作物 请先添加"/>
|
||||
|
||||
<view class="flex gap-1">
|
||||
<wd-button icon="edit" round></wd-button>
|
||||
<wd-button icon="list" round></wd-button>
|
||||
<wd-button icon="edit" round @click="editPatchClick(item)"></wd-button>
|
||||
<wd-button icon="list" round @click="viewDetailClick(item)"></wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-card>
|
||||
|
||||
<wd-card title="2号菜地">
|
||||
<image src="https://wot-ui.cn/assets/redpanda.jpg" mode="aspectFill" />
|
||||
<wd-empty v-if="patchStore.patchList.length === 0" tip="暂无数据"/>
|
||||
|
||||
<template #footer>
|
||||
<view class="flex-1 flex justify-between items-center">
|
||||
<view class="flex flex-col gap-1">
|
||||
<view class="text-base font-medium">
|
||||
黄瓜
|
||||
<text class="text-sm text-gray-400 font-normal ml-1">旱黄瓜</text>
|
||||
</view>
|
||||
<view class="flex gap-1 align-center">
|
||||
<wd-text text="🌱 开花期"/>
|
||||
<wd-text text="📅 2025-04-10"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex gap-1">
|
||||
<wd-button icon="edit" round></wd-button>
|
||||
<wd-button icon="list" round></wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-card>
|
||||
<liu-drag-button v-if="patchStore.showPatchFab" background="#22c55e" @clickBtn="addPatchClick">
|
||||
<wd-icon name="plus" size="24px"></wd-icon>
|
||||
</liu-drag-button>
|
||||
|
||||
<patch-popup />
|
||||
<view class="h-[10px]"></view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -67,9 +66,13 @@
|
||||
import { computed } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import appLogo from '@/static/logo.png'
|
||||
import { usePatchStore } from "@/stores/patch";
|
||||
import type {ICrop, IPatch} from "@/types";
|
||||
import PatchPopup from "@/components/PatchPopup.vue";
|
||||
|
||||
defineOptions({ name: 'Home' })
|
||||
|
||||
const patchStore = usePatchStore()
|
||||
const imgURL = appLogo
|
||||
|
||||
// ===== 时间 =====
|
||||
@@ -90,10 +93,48 @@ const today = computed(() => {
|
||||
return `${m}月${d}日 周${w}`
|
||||
})
|
||||
|
||||
onShow(async () => {
|
||||
await loadData()
|
||||
})
|
||||
|
||||
onShow(() => loadData())
|
||||
async function loadData() {
|
||||
await patchStore.queryPatchList()
|
||||
}
|
||||
|
||||
function loadData() {
|
||||
function addPatchClick() {
|
||||
patchStore.patchForm = {
|
||||
id: 0,
|
||||
name: '',
|
||||
location: '',
|
||||
status: ''
|
||||
}
|
||||
patchStore.patchApiType = 'ADD'
|
||||
patchStore.showPatchPopup = true
|
||||
patchStore.showPatchFab = false
|
||||
}
|
||||
|
||||
function editPatchClick(patch: IPatch) {
|
||||
patchStore.patchForm = JSON.parse(JSON.stringify(patch))
|
||||
patchStore.patchApiType = 'UPDATE'
|
||||
patchStore.showPatchPopup = true
|
||||
patchStore.showPatchFab = false
|
||||
}
|
||||
|
||||
async function viewDetailClick(patch: IPatch) {
|
||||
patchStore.currentPatch = patch
|
||||
|
||||
if (patch.crop) {
|
||||
await patchStore.queryRecordList(patch.crop.id)
|
||||
} else {
|
||||
patchStore.recordList = []
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: '/pages/record/index'
|
||||
})
|
||||
}
|
||||
|
||||
function handlePreview(crop: ICrop) {
|
||||
const fullUrls = crop.images?.map(url => `${patchStore.baseUrl}/${url}`) as string[]
|
||||
uni.previewImage({ urls: fullUrls, current: fullUrls[0] })
|
||||
}
|
||||
</script>
|
||||
|
||||
1
src/pages/record/index.vue
Normal file
1
src/pages/record/index.vue
Normal file
File diff suppressed because one or more lines are too long
@@ -1,105 +1,8 @@
|
||||
<template>
|
||||
<view class="min-h-screen flex flex-col" style="background-image: linear-gradient(180deg, #4CAF50 0%, #8BC34A 42%, #E8F5E9 100%)">
|
||||
<wd-segmented :options="list" v-model:value="current"
|
||||
custom-style="align-self: center; width: 60%; margin-top: 10px;"></wd-segmented>
|
||||
|
||||
<view class="self-center py-3 flex items-center gap-2 text-gray-800">
|
||||
<view class="flex min-w-0 flex-col">
|
||||
<text class="truncate text-lg text-white font-semibold">
|
||||
西红柿
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<wd-card>
|
||||
<template #title>
|
||||
<view class="w-full flex flex-col gap-1.5">
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="text-base font-medium">💧 浇水</view>
|
||||
<wd-tag type="primary" round>🌱 开花期</wd-tag>
|
||||
<view class="text-xs text-gray-400">06-20 08:30</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="flex flex-col gap-2">
|
||||
<text class="text-sm text-gray-500">浇透水,土壤湿度约70%</text>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<wd-card>
|
||||
<template #title>
|
||||
<view class="w-full flex flex-col gap-1.5">
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="text-base font-medium">🌿 施肥</view>
|
||||
<wd-tag type="primary" round>🌱 开花期</wd-tag>
|
||||
<view class="text-xs text-gray-400">06-18 17:00</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="flex flex-col gap-2">
|
||||
<image
|
||||
src="https://wot-ui.cn/assets/panda.jpg"
|
||||
mode="aspectFill"
|
||||
class="rounded-lg"
|
||||
/>
|
||||
<text class="text-sm text-gray-500">追施磷酸二氢钾,800倍叶面喷施</text>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<wd-card>
|
||||
<template #title>
|
||||
<view class="w-full flex flex-col gap-1.5">
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="text-base font-medium">🐛 病虫害防治</view>
|
||||
<wd-tag type="warning" round>⚠️ 发现蚜虫</wd-tag>
|
||||
<view class="text-xs text-gray-400">06-15 09:00</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="flex flex-col gap-2">
|
||||
<text class="text-sm text-gray-500">叶片背面发现少量蚜虫,喷施吡虫啉1000倍液</text>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<wd-card>
|
||||
<template #title>
|
||||
<view class="w-full flex flex-col gap-1.5">
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="text-base font-medium">✂️ 整枝打杈</view>
|
||||
<wd-tag type="primary" round>🌱 开花期</wd-tag>
|
||||
<view class="text-xs text-gray-400">06-12 16:30</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="flex flex-col gap-2">
|
||||
<text class="text-sm text-gray-500">摘除第一穗花以下所有侧枝,保留主蔓</text>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<!-- 04-10 播种(最早) -->
|
||||
<wd-card>
|
||||
<template #title>
|
||||
<view class="w-full flex flex-col gap-1.5">
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="text-base font-medium">🌱 播种</view>
|
||||
<wd-tag type="success" round>📅 苗期</wd-tag>
|
||||
<view class="text-xs text-gray-400">04-10 10:00</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="flex flex-col gap-2">
|
||||
<text class="text-sm text-gray-500">穴盘育苗,覆土1cm,浇透水,放置于温暖通风处</text>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<view class="h-[10px]"></view>
|
||||
<view class="min-h-screen flex flex-col">
|
||||
<wd-text text="我的"/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const list = ref<string[]>(['当前种植', '历史种植'])
|
||||
|
||||
const current = ref('点赞')
|
||||
</script>
|
||||
|
||||
259
src/stores/patch.ts
Normal file
259
src/stores/patch.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type { ICrop, IPatch, IRecord } from "@/types";
|
||||
import type { UploadFile } from "@wot-ui/ui/components/wd-upload/types";
|
||||
|
||||
export const usePatchStore = defineStore('patch', {
|
||||
state: () => ({
|
||||
baseUrl: 'https://cxx0822.s.3q.hair',
|
||||
showPatchFab: true,
|
||||
patchList: [] as IPatch[],
|
||||
patchApiType: 'ADD' as 'ADD' | 'UPDATE' | 'DELETE',
|
||||
patchForm: {
|
||||
|
||||
} as IPatch,
|
||||
showPatchPopup: false,
|
||||
currentPatch: {} as IPatch,
|
||||
recordList: [] as IRecord[],
|
||||
cropApiType: 'ADD' as 'ADD' | 'UPDATE' | 'DELETE',
|
||||
cropForm: {
|
||||
|
||||
} as ICrop,
|
||||
showCropPopup: false,
|
||||
showRecordFab: true,
|
||||
showRecordPopup: false,
|
||||
recordApiType: 'ADD' as 'ADD' | 'UPDATE' | 'DELETE',
|
||||
recordForm: {
|
||||
id: 0,
|
||||
crop_id: 0,
|
||||
type: '',
|
||||
date: '',
|
||||
content: '',
|
||||
images: [],
|
||||
videos: [],
|
||||
stage: '',
|
||||
} as IRecord,
|
||||
imageFileList: [] as UploadFile[],
|
||||
videoFileList: [] as UploadFile[],
|
||||
}),
|
||||
actions: {
|
||||
async queryPatchList() {
|
||||
this.patchList = await this.queryPatchListApi()
|
||||
},
|
||||
async queryPatch(patchId: number) {
|
||||
this.currentPatch = await this.queryPatchApi(patchId)
|
||||
},
|
||||
async queryRecordList(cropId: number) {
|
||||
this.recordList = await this.queryRecordListApi(cropId)
|
||||
},
|
||||
async queryPatchListApi() {
|
||||
return new Promise<IPatch[]>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/patches`,
|
||||
method: 'GET',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(res.data as IPatch[])
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: res.data })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async queryPatchApi(patchId: number) {
|
||||
return new Promise<IPatch>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/patches/${patchId}`,
|
||||
method: 'GET',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(res.data as IPatch)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: res.data })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async addPatchApi() {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/patches`,
|
||||
method: 'POST',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
data: this.patchForm,
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async updatePatchApi(patchId: number) {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/patches/${patchId}`,
|
||||
method: 'PUT',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
data: this.patchForm,
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async deletePatchApi(patchId: number) {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/patches/${patchId}`,
|
||||
method: 'DELETE',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async addCropApi() {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/crops`,
|
||||
method: 'POST',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
data: this.cropForm,
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async updateCropApi(cropId: number) {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/crops/${cropId}`,
|
||||
method: 'PUT',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
data: this.cropForm,
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async deleteCropApi(cropId: number) {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/crops/${cropId}`,
|
||||
method: 'DELETE',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async queryRecordListApi(cropId: number) {
|
||||
return new Promise<IRecord[]>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/records/crop/${cropId}`,
|
||||
method: 'GET',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(res.data as IRecord[])
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: res.data })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async addRecordApi() {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/records`,
|
||||
method: 'POST',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
data: this.recordForm,
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async updateRecordApi(recordId: number) {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/records/${recordId}`,
|
||||
method: 'PUT',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
data: this.recordForm,
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
async deleteRecordApi(recordId: number) {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${this.baseUrl}/patch-api/records/${recordId}`,
|
||||
method: 'DELETE',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
reject({ status: res.statusCode, data: false })
|
||||
}
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
27
src/types/index.ts
Normal file
27
src/types/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export interface ICrop {
|
||||
id: number
|
||||
patch_id: number
|
||||
name: string
|
||||
variety: string
|
||||
date: string
|
||||
images?: string[]
|
||||
}
|
||||
|
||||
export interface IPatch {
|
||||
id: number
|
||||
name: string
|
||||
location: string
|
||||
status: string
|
||||
crop?: ICrop
|
||||
}
|
||||
|
||||
export interface IRecord {
|
||||
id: number
|
||||
crop_id: number
|
||||
type: string
|
||||
date: string
|
||||
content: string
|
||||
images: string[]
|
||||
videos: string[]
|
||||
stage: string
|
||||
}
|
||||
12
src/uni_modules/liu-drag-button_1.0.5/changelog.md
Normal file
12
src/uni_modules/liu-drag-button_1.0.5/changelog.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## 1.0.5(2023-07-13)
|
||||
优化
|
||||
## 1.0.4(2023-06-08)
|
||||
增加预览二维码
|
||||
## 1.0.3(2023-05-31)
|
||||
增加license
|
||||
## 1.0.2(2023-04-28)
|
||||
优化
|
||||
## 1.0.1(2023-04-26)
|
||||
增加示例
|
||||
## 1.0.0(2023-04-26)
|
||||
初始化发布
|
||||
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<view>
|
||||
<movable-area class="movable-area" :scale-area="false">
|
||||
<movable-view class="movable-view" :class="!isRemove?'animation-info':''" style="pointer-events: auto;"
|
||||
:style="{ background: background }"
|
||||
@click="clickBtn" @touchstart="touchstart" @touchend="touchend" @change="onChange" direction="all"
|
||||
inertia="true" :x="x" :y="y" :disabled="disabled" :out-of-bounds="true" :damping="200" :friction="100">
|
||||
<slot></slot>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
background: {
|
||||
type: String,
|
||||
default: "#8b5cf6"
|
||||
},
|
||||
//是否禁用拖动
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//是否自动停靠
|
||||
canDocking: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
//按钮默认位置离底部距离(px)
|
||||
bottomPx: {
|
||||
type: Number,
|
||||
default: 30
|
||||
},
|
||||
//按钮默认位置离右边距离(px)
|
||||
rightPx: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
left: 0,
|
||||
top: 0,
|
||||
isRemove: true,
|
||||
windowWidth: 0,
|
||||
windowHeight: 0,
|
||||
btnWidth: 0,
|
||||
btnHeight: 0,
|
||||
x: 10000,
|
||||
y: 10000,
|
||||
old: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getSysInfo()
|
||||
},
|
||||
methods: {
|
||||
getSysInfo() {
|
||||
let sysInfo = uni.getSystemInfoSync()
|
||||
this.windowWidth = sysInfo.windowWidth
|
||||
this.windowHeight = sysInfo.windowHeight
|
||||
let view = uni.createSelectorQuery().in(this).select(".movable-view")
|
||||
view.boundingClientRect(rect => {
|
||||
this.btnWidth = rect.width
|
||||
this.btnHeight = rect.height
|
||||
this.x = this.old.x
|
||||
this.y = this.old.y
|
||||
this.$nextTick(res => {
|
||||
this.x = this.windowWidth - this.btnWidth - this.rightPx
|
||||
this.y = this.windowHeight - this.btnHeight - this.bottomPx
|
||||
})
|
||||
}).exec()
|
||||
},
|
||||
//移动按钮
|
||||
onChange(e) {
|
||||
this.old.x = e.detail.x
|
||||
this.old.y = e.detail.y
|
||||
},
|
||||
//开始移动
|
||||
touchstart(e) {
|
||||
this.isRemove = true
|
||||
},
|
||||
//结束移动
|
||||
touchend(e) {
|
||||
if (this.canDocking && this.old.x) {
|
||||
this.x = this.old.x
|
||||
this.y = this.old.y
|
||||
let bWidth = (this.windowWidth - this.btnWidth) / 2
|
||||
if (this.x < 0 || (this.x > 0 && this.x <= bWidth)) {
|
||||
this.$nextTick(res => {
|
||||
this.x = 0
|
||||
})
|
||||
} else {
|
||||
this.$nextTick(res => {
|
||||
this.x = this.windowWidth - this.btnWidth
|
||||
})
|
||||
}
|
||||
this.isRemove = false
|
||||
}
|
||||
},
|
||||
//点击按钮
|
||||
clickBtn() {
|
||||
this.$emit('clickBtn')
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.movable-view {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
box-shadow: 0px 4rpx 12rpx 0px rgba(15, 23, 42, 0.08);
|
||||
border-radius: 50rpx;
|
||||
color: #FFFFFF;
|
||||
font-size: 26rpx;
|
||||
touch-action: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.animation-info {
|
||||
transition: left .25s ease;
|
||||
}
|
||||
|
||||
.movable-area {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999999 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
6
src/uni_modules/liu-drag-button_1.0.5/license.md
Normal file
6
src/uni_modules/liu-drag-button_1.0.5/license.md
Normal file
@@ -0,0 +1,6 @@
|
||||
### 1、本插件可免费下载使用;
|
||||
### 2、未经许可,严禁复制本插件派生同类插件上传插件市场;
|
||||
### 3、未经许可,严禁在插件市场恶意复制抄袭本插件进行违规获利;
|
||||
### 4、对本软件的任何使用都必须遵守这些条款,违反这些条款的个人或组织将面临法律追究。
|
||||
|
||||
|
||||
85
src/uni_modules/liu-drag-button_1.0.5/package.json
Normal file
85
src/uni_modules/liu-drag-button_1.0.5/package.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"id": "liu-drag-button",
|
||||
"displayName": "可拖动悬浮按钮",
|
||||
"version": "1.0.5",
|
||||
"description": "可拖动的悬浮按钮,兼容小程序、H5,支持自动停靠,支持自定义样式,使用相当简单,源码简单易修改。",
|
||||
"keywords": [
|
||||
"悬浮按钮",
|
||||
"拖拽按钮",
|
||||
"拖动",
|
||||
"按钮",
|
||||
"拖拽"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "u"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "u",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/uni_modules/liu-drag-button_1.0.5/readme.md
Normal file
36
src/uni_modules/liu-drag-button_1.0.5/readme.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# liu-drag-button适用于uni-app项目的可拖动悬浮按钮组件
|
||||
### 本组件目前兼容微信小程序、H5
|
||||
### 本组件是可拖动的悬浮按钮,兼容小程序、H5,支持自动停靠,支持自定义样式,源码简单易修改
|
||||
# --- 扫码预览、关注我们 ---
|
||||
|
||||
## 扫码关注公众号,查看更多插件信息,预览插件效果!
|
||||
|
||||

|
||||
|
||||
### 使用方式
|
||||
``` html
|
||||
<liu-drag-button @clickBtn="clickBtn">按钮</liu-drag-button>
|
||||
```
|
||||
``` javascript
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
//点击按钮
|
||||
clickBtn(){
|
||||
console.log('按钮被点击了')
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 属性说明
|
||||
| 名称 | 类型 | 默认值 | 描述 |
|
||||
| ----------------------------|--------------- | ------------------ | ---------------|
|
||||
| disabled | Boolean | false | 是否禁用拖动
|
||||
| canDocking | Boolean | true | 是否自动停靠
|
||||
| bottomPx | Number | 30 | 按钮默认位置离底部距离(px)
|
||||
| rightPx | Number | 0 | 按钮默认位置离右边距离(px)
|
||||
19
src/utils/index.ts
Normal file
19
src/utils/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export function formatDate(ts: string | number) {
|
||||
if (!ts)
|
||||
return ''
|
||||
const d = new Date(ts)
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
|
||||
}
|
||||
|
||||
export function formatDateTime(ts: string | number) {
|
||||
if (!ts)
|
||||
return ''
|
||||
const d = new Date(ts)
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||
}
|
||||
|
||||
export function generateUniqueId() {
|
||||
return Date.now() + '-' + Math.random().toString(36).slice(2, 6);
|
||||
}
|
||||
Reference in New Issue
Block a user