| | |
| | | "devDependencies": { |
| | | "@antfu/eslint-config": "^5.0.0", |
| | | "@iconify-json/icon-park-outline": "^1.2.2", |
| | | "@iconify-json/icon-park-solid": "^1.2.4", |
| | | "@iconify-json/ri": "^1.2.10", |
| | | "@iconify/vue": "^5.0.0", |
| | | "@types/node": "^24.1.0", |
| | | "@vitejs/plugin-vue": "^6.0.1", |
| New file |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024"><path d="M270.1 741.7c0 23.4 19.1 42.5 42.6 42.5h48.7v120.4c0 30.5 24.5 55.4 54.6 55.4c30.2 0 54.6-24.8 54.6-55.4V784.1h85v120.4c0 30.5 24.5 55.4 54.6 55.4c30.2 0 54.6-24.8 54.6-55.4V784.1h48.7c23.5 0 42.6-19.1 42.6-42.5V346.4h-486v395.3zm357.1-600.1l44.9-65c2.6-3.8 2-8.9-1.5-11.4c-3.5-2.4-8.5-1.2-11.1 2.6l-46.6 67.6c-30.7-12.1-64.9-18.8-100.8-18.8c-35.9 0-70.1 6.7-100.8 18.8l-46.6-67.5c-2.6-3.8-7.6-5.1-11.1-2.6c-3.5 2.4-4.1 7.4-1.5 11.4l44.9 65c-71.4 33.2-121.4 96.1-127.8 169.6h486c-6.6-73.6-56.7-136.5-128-169.7zM409.5 244.1a26.9 26.9 0 1 1 26.9-26.9a26.97 26.97 0 0 1-26.9 26.9zm208.4 0a26.9 26.9 0 1 1 26.9-26.9a26.97 26.97 0 0 1-26.9 26.9zm223.4 100.7c-30.2 0-54.6 24.8-54.6 55.4v216.4c0 30.5 24.5 55.4 54.6 55.4c30.2 0 54.6-24.8 54.6-55.4V400.1c.1-30.6-24.3-55.3-54.6-55.3zm-658.6 0c-30.2 0-54.6 24.8-54.6 55.4v216.4c0 30.5 24.5 55.4 54.6 55.4c30.2 0 54.6-24.8 54.6-55.4V400.1c0-30.6-24.5-55.3-54.6-55.3z" fill="currentColor"></path></svg> |
| | |
| | | pid: 202, |
| | | }, |
| | | { |
| | | name: 'addAppVersion', |
| | | path: '/system/appVersion/appVersionManage/add', |
| | | title: '新增APP版本号', |
| | | requiresAuth: true, |
| | | icon: 'icon-park-outline:tool', |
| | | menuType: 'page', |
| | | componentPath: '/system/appVersion/appVersionManage/add.vue', |
| | | id: 2023, |
| | | pid: 202, |
| | | hide: true, |
| | | activeMenu: '/system/appVersion/appVersionManage', |
| | | }, |
| | | { |
| | | name: 'systemConfig', |
| | | path: '/system/config', |
| | | title: '系统配置', |
| New file |
| | |
| | | <script setup lang="tsx"> |
| | | import { ref } from 'vue' |
| | | import { useRouter } from 'vue-router' |
| | | import { useMessage } from 'naive-ui' |
| | | import type { UploadFileInfo, FormRules, FormInst } from 'naive-ui' |
| | | import { customUploadRequest, beforeUploadCheck } from '@/utils/upload' |
| | | import { releaseAppVersion } from '@/service/api/appVersion' |
| | | |
| | | const router = useRouter() |
| | | const message = useMessage() |
| | | const formRef = ref<FormInst | null>(null) |
| | | |
| | | const formModel = ref({ |
| | | version: '', |
| | | androidFiles: [] as UploadFileInfo[], |
| | | iosFiles: [] as UploadFileInfo[], |
| | | updateNote: '', |
| | | isForceUpdate: false |
| | | }) |
| | | |
| | | const rules: FormRules = { |
| | | version: [ |
| | | { required: true, message: '请输入版本号', trigger: ['blur', 'input'] } |
| | | ], |
| | | androidFiles: [ |
| | | { required: true, type: 'array', message: '请上传安卓安装包', trigger: ['blur', 'change'] } |
| | | ], |
| | | iosFiles: [ |
| | | { required: true, type: 'array', message: '请上传 iOS 安装包', trigger: ['blur', 'change'] } |
| | | ], |
| | | updateNote: [ |
| | | { required: true, message: '请输入更新说明', trigger: ['blur', 'input'] } |
| | | ] |
| | | } |
| | | |
| | | function beforeUploadApk(data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) { |
| | | return beforeUploadCheck(data, { maxSize: 500, allowedTypes: [] }) |
| | | } |
| | | |
| | | function beforeUploadIpa(data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) { |
| | | return beforeUploadCheck(data, { maxSize: 500, allowedTypes: [] }) |
| | | } |
| | | |
| | | async function customRequest(options: any) { |
| | | await customUploadRequest(options) |
| | | } |
| | | |
| | | async function handleConfirm() { |
| | | await formRef.value?.validate(async (errors) => { |
| | | if (!errors) { |
| | | const params = { |
| | | version: formModel.value.version, |
| | | androidFile: formModel.value.androidFiles[0]?.url || '', |
| | | iosFile: formModel.value.iosFiles[0]?.url || '', |
| | | updateDescription: formModel.value.updateNote, |
| | | forceUpdateFlag: formModel.value.isForceUpdate ? 1 : 0 |
| | | } |
| | | |
| | | if (!params.androidFile || !params.iosFile) { |
| | | message.error('请等待文件上传完成') |
| | | return |
| | | } |
| | | |
| | | const { isSuccess } = await releaseAppVersion(params) |
| | | if (isSuccess) { |
| | | message.success('发布成功') |
| | | router.back() |
| | | } |
| | | } else { |
| | | message.error('请完善必填信息') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | function handleCancel() { |
| | | router.back() |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="h-full flex flex-col bg-white rounded-md"> |
| | | <div class="flex items-center px-6 pt-6 mb-6"> |
| | | <div class="flex items-center relative pl-3"> |
| | | <div class="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-4 bg-[#0068FF] rounded-sm"></div> |
| | | <span class="text-base font-bold text-[#1D2129] ml-2">新增APP版本号</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="flex-1 overflow-y-auto px-6"> |
| | | <n-form |
| | | ref="formRef" |
| | | :model="formModel" |
| | | :rules="rules" |
| | | label-placement="left" |
| | | inline |
| | | require-mark-placement="left" |
| | | :show-feedback="false" |
| | | class="max-w-4xl flex flex-col gap-16px" |
| | | > |
| | | <n-form-item label="版本号" path="version"> |
| | | <n-input v-model:value="formModel.version" placeholder="请输入版本号" class="!w-[400px]" /> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="安卓文件" path="androidFiles"> |
| | | <div class="flex flex-col gap-2"> |
| | | <n-upload |
| | | v-model:file-list="formModel.androidFiles" |
| | | :max="1" |
| | | accept=".apk" |
| | | :on-before-upload="beforeUploadApk" |
| | | :custom-request="customRequest" |
| | | :show-file-list="false" |
| | | > |
| | | <n-button style="width: 100px"> |
| | | <template #icon> |
| | | <icon-park-outline-upload /> |
| | | </template> |
| | | {{ formModel.androidFiles.length > 0 ? '重新上传' : '点击上传' }} |
| | | </n-button> |
| | | </n-upload> |
| | | |
| | | <div v-if="formModel.androidFiles.length > 0" class="flex items-center bg-[#F5F6F8] px-3 py-2 rounded w-fit gap-3"> |
| | | <svg-icons-android-filled class="text-[#00B42A] text-lg" /> |
| | | <span class="text-sm text-[#1D2129] max-w-[200px] truncate" :title="formModel.androidFiles[0].name">{{ formModel.androidFiles[0].name }}</span> |
| | | <icon-park-outline-close class="text-gray-400 cursor-pointer hover:text-gray-600" @click="formModel.androidFiles = []" /> |
| | | </div> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="iOS文件" path="iosFiles"> |
| | | <div class="flex flex-col gap-2"> |
| | | <n-upload |
| | | v-model:file-list="formModel.iosFiles" |
| | | :max="1" |
| | | accept=".ipa" |
| | | :on-before-upload="beforeUploadIpa" |
| | | :custom-request="customRequest" |
| | | :show-file-list="false" |
| | | > |
| | | <n-button style="width: 100px"> |
| | | <template #icon> |
| | | <icon-park-outline-upload /> |
| | | </template> |
| | | {{ formModel.iosFiles.length > 0 ? '重新上传' : '点击上传' }} |
| | | </n-button> |
| | | </n-upload> |
| | | |
| | | <div v-if="formModel.iosFiles.length > 0" class="flex items-center bg-[#F5F6F8] px-3 py-2 rounded w-fit gap-3"> |
| | | <ri-apple-fill class="text-black text-lg" /> |
| | | <span class="text-sm text-[#1D2129] max-w-[200px] truncate" :title="formModel.iosFiles[0].name">{{ formModel.iosFiles[0].name }}</span> |
| | | <icon-park-outline-close class="text-gray-400 cursor-pointer hover:text-gray-600" @click="formModel.iosFiles = []" /> |
| | | </div> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="更新说明" path="updateNote" label-placement="top"> |
| | | <n-input |
| | | v-model:value="formModel.updateNote" |
| | | type="textarea" |
| | | placeholder="请输入" |
| | | :autosize="{ minRows: 6, maxRows: 10 }" |
| | | show-count |
| | | style="width: 750px;" |
| | | :maxlength="200" |
| | | /> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="强制更新" path="isForceUpdate" label-placement="left"> |
| | | <n-switch v-model:value="formModel.isForceUpdate" /> |
| | | </n-form-item> |
| | | </n-form> |
| | | </div> |
| | | |
| | | <div class="flex justify-end gap-3 mt-auto px-6 py-4 border-t border-gray-100"> |
| | | <n-button style="padding: 8px 29px;" @click="handleCancel">返回</n-button> |
| | | <n-button style="padding: 8px 29px;" type="primary" @click="handleConfirm">保存</n-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | </style> |
| | |
| | | <script setup lang="tsx"> |
| | | import { ref, computed, reactive, onMounted } from 'vue' |
| | | import { useRouter } from 'vue-router' |
| | | import type { DataTableColumns, FormInst, FormRules, UploadFileInfo } from 'naive-ui' |
| | | import { NButton } from 'naive-ui' |
| | | import dayjs from 'dayjs' |
| | |
| | | import { fetchAppVersionPage, releaseAppVersion } from '@/service/api/appVersion' |
| | | import type { AppVersionItem } from '@/service/api/appVersion' |
| | | |
| | | const router = useRouter() |
| | | const listData = ref<AppVersionItem[]>([]) |
| | | const loading = ref(false) |
| | | const showForm = ref(false) |
| | |
| | | { |
| | | title: '序号', |
| | | key: 'index', |
| | | width: 80, |
| | | align: 'center', |
| | | width: 60, |
| | | align: 'left', |
| | | render: (_row, rowIndex) => rowIndex + 1 + (pagination.page - 1) * pagination.pageSize |
| | | }, |
| | | { |
| | | title: '版本号', |
| | | key: 'version', |
| | | minWidth: 140, |
| | | width: 168, |
| | | align: 'left', |
| | | ellipsis: true |
| | | }, |
| | | { |
| | | title: '更新提示', |
| | | key: 'updateDescription', |
| | | minWidth: 220, |
| | | align: 'left', |
| | | ellipsis: { tooltip: true } |
| | | }, |
| | | { |
| | | title: '创建时间', |
| | | key: 'createTime', |
| | | minWidth: 160, |
| | | width: 180, |
| | | align: 'left', |
| | | render: (row) => row.createTime ? dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') : '-' |
| | | }, |
| | | { |
| | | title: '操作', |
| | | key: 'actions', |
| | | width: 120, |
| | | width: 80, |
| | | align: 'left', |
| | | render: (row) => { |
| | | return ( |
| | | <NButton v-permission="20222" text type="primary" onClick={() => handleView(row)}> |
| | |
| | | } |
| | | |
| | | function handleAdd() { |
| | | formMode.value = 'add' |
| | | viewingId.value = null |
| | | resetForm() |
| | | showForm.value = true |
| | | router.push({ name: 'addAppVersion' }) |
| | | } |
| | | |
| | | function handleView(row: AppVersionItem) { |
| | |
| | | |
| | | <template> |
| | | <div class="h-full flex flex-col"> |
| | | <n-card style="border-radius: 6px" content-style="padding: 12px"> |
| | | <div class="mb-4 flex justify-end" v-permission="20221"> |
| | | <n-button style="height: 32px; background-color: #E6F4FF; color: #0068FF; border: 1px solid #7ABDFF" @click="handleAdd"> |
| | | 新增版本号 |
| | | </n-button> |
| | | <n-card style="border-radius: 6px" content-style="padding: 24px"> |
| | | <div class="flex items-center justify-between mb-6"> |
| | | <div class="flex items-center relative pl-3"> |
| | | <div class="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-4 bg-[#0068FF] rounded-sm"></div> |
| | | <span class="text-base font-bold text-[#1D2129] ml-2">APP版本管理</span> |
| | | </div> |
| | | <div v-permission="20221"> |
| | | <n-button style="height: 32px; background-color: #E6F4FF; color: #0068FF; border: 1px solid #7ABDFF" @click="handleAdd"> |
| | | 新增版本号 |
| | | </n-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <n-data-table |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="h-full flex flex-col"> |
| | | <n-card content-style="padding: 12px"> |
| | | <n-form |
| | | ref="formRef" |
| | | <div class="h-full flex flex-col bg-white"> |
| | | <!-- Content --> |
| | | <div class="flex-1 p-6 overflow-y-auto"> |
| | | <!-- Header --> |
| | | <div class="flex items-center gap-2 mb-6"> |
| | | <div class="w-1 h-4 bg-blue-title rounded-sm"></div> |
| | | <span class="text-base font-bold text-gray-800">系统维护</span> |
| | | </div> |
| | | |
| | | <n-form |
| | | ref="formRef" |
| | | :model="formModel" |
| | | :rules="rules" |
| | | :rules="rules" |
| | | label-placement="left" |
| | | inline |
| | | :show-feedback="false" |
| | | inline |
| | | require-mark-placement="left" |
| | | :show-feedback="false" |
| | | > |
| | | <n-space vertical :size="16"> |
| | | <n-form-item label="维护状态" required> |
| | | <n-form-item label="维护状态" path="maintenanceStatus" required> |
| | | <n-switch v-model:value="formModel.maintenanceStatus" /> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="开始维护时间" path="startTime" required> |
| | | <n-form-item label="开始维护时间" path="startTime"> |
| | | <n-date-picker |
| | | v-model:value="formModel.startTime" |
| | | type="datetime" |
| | | placeholder="年/月/日 --:--" |
| | | style="width: 400px;" |
| | | placeholder="请选择日期" |
| | | style="width: 240px;" |
| | | clearable |
| | | /> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="预计结束时间" path="endTime" required> |
| | | <n-form-item label="预计结束时间" path="endTime"> |
| | | <n-date-picker |
| | | v-model:value="formModel.endTime" |
| | | type="datetime" |
| | | placeholder="年/月/日 --:--" |
| | | style="width: 400px;" |
| | | placeholder="请选择日期" |
| | | style="width: 240px;" |
| | | clearable |
| | | /> |
| | | </n-form-item> |
| | | </n-space> |
| | | </n-form> |
| | | </n-card> |
| | | </div> |
| | | |
| | | <div v-permission="20211" class="absolute bottom-0 left-0 right-0 flex justify-end gap-3 bg-white px-6 py-4" style="box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1); z-index: 100"> |
| | | <n-button type="primary" style="width: 84px; height: 32px" @click="handleSave">保存</n-button> |
| | | <!-- Footer --> |
| | | <div |
| | | v-permission="20211" |
| | | class="flex justify-end gap-3 bg-white px-6 py-4 border-t border-gray-100" |
| | | style="box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1); z-index: 100" |
| | | > |
| | | <n-button type="primary" style="width: 84px; height: 32px" @click="handleSave">保存</n-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .bg-blue-title{ |
| | | background: #0068FF; |
| | | } |
| | | </style> |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="h-full flex flex-col"> |
| | | <n-card content-style="padding: 12px"> |
| | | <div class="h-full flex flex-col bg-white"> |
| | | <!-- Content --> |
| | | <div class="flex-1 p-6 overflow-y-auto"> |
| | | <!-- Header --> |
| | | <div class="flex items-center gap-2" style="margin-bottom: 16px;"> |
| | | <div class="w-1 h-4 bg-blue-title rounded-sm"></div> |
| | | <span class="text-base font-bold text-gray-800">系统配置</span> |
| | | </div> |
| | | |
| | | <n-form |
| | | ref="formRef" |
| | | :model="formModel" |
| | |
| | | <n-input |
| | | v-model:value="formModel.customerServicePhone" |
| | | placeholder="请输入" |
| | | style="width: 300px;" |
| | | style="width: 290px;" |
| | | /> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="距点位" path="distanceThreshold" required> |
| | | <n-input-number |
| | | v-model:value="formModel.distanceThreshold" |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | class="mr-2" |
| | | /> |
| | | m内可下单 |
| | | <div class="flex items-center gap-2"> |
| | | <n-input-number |
| | | v-model:value="formModel.distanceThreshold" |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | style="width: 303px;" |
| | | /> |
| | | <span>m内可下单</span> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="起终点之间" path="timeThreshold" required> |
| | | <n-input-number |
| | | v-model:value="formModel.timeThreshold" |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | class="mr-2" |
| | | /> |
| | | m内不可下单 |
| | | <div class="flex items-center gap-2"> |
| | | <n-input-number |
| | | v-model:value="formModel.timeThreshold" |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | style="width: 277px;" |
| | | /> |
| | | <span>m内不可下单</span> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="距点位" path="attractDistance" required> |
| | | <n-input-number |
| | | v-model:value="formModel.attractDistance" |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | class="mr-2" |
| | | /> |
| | | m内吸附 |
| | | <div class="flex items-center gap-2"> |
| | | <n-input-number |
| | | v-model:value="formModel.attractDistance" |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | style="width: 303px;" |
| | | /> |
| | | <span>m内吸附</span> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="到达目的地后" path="destinationDistance" required> |
| | | <n-input-number |
| | | v-model:value="formModel.destinationDistance" |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | class="mr-2" |
| | | /> |
| | | 分钟自动结束行程 |
| | | <div class="flex items-center gap-2"> |
| | | <n-input-number |
| | | v-model:value="formModel.destinationDistance" |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | style="width: 264px;" |
| | | /> |
| | | <span>分钟自动结束行程</span> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="小程序地图默认定位" required> |
| | | <n-input |
| | | v-model:value="formModel.miniProgramLongitude" |
| | | placeholder="请输入经度" |
| | | style="width: 200px;" |
| | | class="mr-2" |
| | | /> |
| | | <n-input |
| | | v-model:value="formModel.miniProgramLatitude" |
| | | placeholder="请输入纬度" |
| | | style="width: 200px;" |
| | | class="mr-2" |
| | | /> |
| | | <span class="text-gray-500">地址位置地址位置1</span> |
| | | <div class="flex items-center gap-2"> |
| | | <n-input |
| | | v-model:value="formModel.miniProgramLongitude" |
| | | placeholder="请输入经度" |
| | | style="width: 225px;" |
| | | /> |
| | | <n-input |
| | | v-model:value="formModel.miniProgramLatitude" |
| | | placeholder="请输入纬度" |
| | | style="width: 225px;" |
| | | /> |
| | | <span class="text-gray-500">地址信息地址信息地址信息</span> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="常见问题首页展示数" path="faqDisplayCount" required> |
| | |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :show-button="false" |
| | | style="width: 225px;" |
| | | /> |
| | | </n-form-item> |
| | | </n-space> |
| | | </n-form> |
| | | </n-card> |
| | | </div> |
| | | |
| | | <div v-permission="2031" class="absolute bottom-0 left-0 right-0 flex justify-end gap-3 bg-white px-6 py-4" style="box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1); z-index: 100"> |
| | | <n-button type="primary" style="width: 84px; height: 32px" @click="handleSave">保存</n-button> |
| | | </div> |
| | | <!-- Footer --> |
| | | <div |
| | | v-permission="2031" |
| | | class="flex justify-end gap-3 bg-white px-6 py-4 border-t border-gray-100" |
| | | style="box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1); z-index: 100" |
| | | > |
| | | <n-button type="primary" style="width: 84px; height: 32px" @click="handleSave">保存</n-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .bg-blue-title { |
| | | background: #0068FF; |
| | | } |
| | | </style> |