fix: 修改bug,首页门店没过滤删除的
This commit is contained in:
6
.env.dev
6
.env.dev
@@ -4,14 +4,14 @@ NODE_ENV=production
|
||||
VITE_DEV=true
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='http://localhost:48080'
|
||||
#VITE_BASE_URL='https://test.zmingzhikeji.cn'
|
||||
#VITE_BASE_URL='http://localhost:48080'
|
||||
VITE_BASE_URL='https://api.tuanbanlv.com'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||
VITE_UPLOAD_TYPE=client
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='https://www.zmingzhikeji.cn/admin-api/infra/file/upload'
|
||||
VITE_UPLOAD_URL='https://api.tuanbanlv.com/admin-api/infra/file/upload'
|
||||
|
||||
# 接口地址
|
||||
VITE_API_URL=/admin-api
|
||||
|
||||
Submodule ruoyi-vue-pro updated: 1be18e2ff5...39edcaadf4
@@ -95,5 +95,26 @@ export const RenewalOrderApi = {
|
||||
// 清空订单合同与客户签名(重新生成合同时先清空再扫码签名)
|
||||
clearContractAndSignature: async (id: number) => {
|
||||
return await request.post({ url: `/car/renewal-order/clear-contract-sign`, params: { id } })
|
||||
},
|
||||
|
||||
// 身份证识别(用于图片识别新增)
|
||||
recognizeIdcard: async (file: File, idCardSide: 'front' | 'back' = 'front') => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
formData.append('idCardSide', idCardSide)
|
||||
return await request.upload({
|
||||
url: '/car/renewal-order/recognize-idcard',
|
||||
data: formData
|
||||
})
|
||||
},
|
||||
|
||||
// 机动车销售发票识别(用于图片识别新增)
|
||||
recognizeVehicleInvoice: async (file: File) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return await request.upload({
|
||||
url: '/car/renewal-order/recognize-vehicle-invoice',
|
||||
data: formData
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ export interface PermissionAssignRoleDataScopeReqVO {
|
||||
roleId: number
|
||||
dataScope: number
|
||||
dataScopeDeptIds: number[]
|
||||
storeDataScope?: number // 门店数据范围(1:全部门店 2:本门店及以下 3:仅本人 4:指定门店)
|
||||
storeDataScopeStoreIds?: number[] // 指定门店ID列表(storeDataScope=4 时使用)
|
||||
}
|
||||
|
||||
// 查询角色拥有的菜单权限
|
||||
|
||||
@@ -9,6 +9,8 @@ export interface RoleVO {
|
||||
type: number
|
||||
dataScope: number
|
||||
dataScopeDeptIds: number[]
|
||||
storeDataScope?: number // 门店数据范围(1:全部门店 2:本门店及以下 3:仅本人 4:指定门店)
|
||||
storeDataScopeStoreIds?: number[] // 指定门店ID列表(storeDataScope=4 时使用)
|
||||
createTime: Date
|
||||
}
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ service.interceptors.response.use(
|
||||
} else {
|
||||
ElNotification.error({ title: msg })
|
||||
}
|
||||
return Promise.reject('error')
|
||||
return Promise.reject(new Error(msg))
|
||||
} else {
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ export const SystemDataScopeEnum = {
|
||||
DEPT_SELF: 5 // 仅本人数据权限
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户的社交平台的类型枚举
|
||||
*/
|
||||
|
||||
@@ -252,5 +252,6 @@ export enum DICT_TYPE {
|
||||
CAR_RENEWAL_PRODUCT_TYPE= 'car_renewal_product_type',
|
||||
CAR_RENEWAL_PAY_METHOD= 'car_renewal_pay_method',
|
||||
CAR_RENEWAL_YEAR= 'car_renewal_year', // 续保生效年限
|
||||
CAR_STORE_DATA_SCOPE = 'car_store_data_scope' // 门店数据权限(1 全部门店 2 本门店及以下)
|
||||
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="购置税凭证" prop="purchaseTaxInvoiceUrls" required>
|
||||
<el-form-item label="购置税凭证" prop="purchaseTaxInvoiceUrls">
|
||||
<UploadImgs v-model="formData.purchaseTaxInvoiceUrls" :limit="10" :file-size="10" :file-type="['image/jpeg', 'image/png', 'image/jpg', 'image/gif']" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -182,6 +182,7 @@
|
||||
placeholder="请选择门店"
|
||||
filterable
|
||||
style="width: 100%"
|
||||
@change="handleStoreChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="store in storeList"
|
||||
@@ -538,14 +539,6 @@ const formRules = reactive({
|
||||
},
|
||||
trigger: 'change'
|
||||
}],
|
||||
purchaseTaxInvoiceUrls: [{
|
||||
required: true,
|
||||
validator: (_: any, val: string[] | undefined, cb: (e?: Error) => void) => {
|
||||
if (!val || !Array.isArray(val) || val.length === 0) cb(new Error('请上传购置税凭证'))
|
||||
else cb()
|
||||
},
|
||||
trigger: 'change'
|
||||
}],
|
||||
idCardFrontUrl: [{ required: true, message: '请上传身份证正面', trigger: 'change' }],
|
||||
idCardBackUrl: [{ required: true, message: '请上传身份证反面', trigger: 'change' }]
|
||||
})
|
||||
@@ -562,6 +555,14 @@ const getStoreList = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择门店时,服务购买方自动填充为门店名称 */
|
||||
const handleStoreChange = (storeId: number) => {
|
||||
if (storeId) {
|
||||
const store = storeList.value.find(s => s.id === storeId)
|
||||
if (store?.storeName) formData.value.serviceBuyer = store.storeName
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取续保产品列表 */
|
||||
const getProductList = async () => {
|
||||
try {
|
||||
@@ -605,7 +606,7 @@ const open = async (type: string, id?: number) => {
|
||||
resetForm()
|
||||
// 加载门店列表和产品列表
|
||||
await Promise.all([getStoreList(), getProductList()])
|
||||
// 新增:默认选择门店第一项,车辆购买方默认为该门店名称(编辑不覆盖,均可修改)
|
||||
// 新增:默认选择门店第一项,服务购买方默认为门店名称(编辑不覆盖,均可修改)
|
||||
if (!id && storeList.value.length > 0) {
|
||||
if (!formData.value.storeId) {
|
||||
formData.value.storeId = storeList.value[0].id
|
||||
@@ -748,7 +749,31 @@ const openCopy = async (id: number) => {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
defineExpose({ open, openCopy }) // 提供 open / openCopy 方法,用于打开弹窗
|
||||
/** 打开弹窗并预填识别结果(身份证 / 车辆购置发票) */
|
||||
const openWithRecognizedData = async (recognizedData: Record<string, any>) => {
|
||||
await open('create')
|
||||
if (!recognizedData) return
|
||||
// 服务购买方 = 门店名字,不由识别结果填充;车辆购买方 = 身份证上的人名
|
||||
const idCardName = recognizedData.carBuyer || recognizedData.serviceBuyer || recognizedData.name
|
||||
if (idCardName) formData.value.carBuyer = String(idCardName)
|
||||
if (recognizedData.certNo) formData.value.certNo = String(recognizedData.certNo)
|
||||
if (recognizedData.contactAddress) formData.value.contactAddress = String(recognizedData.contactAddress)
|
||||
// 车辆购置发票识别字段
|
||||
if (recognizedData.vin) formData.value.vin = String(recognizedData.vin)
|
||||
if (recognizedData.engineNo) formData.value.engineNo = String(recognizedData.engineNo)
|
||||
if (recognizedData.factoryModel) formData.value.factoryModel = String(recognizedData.factoryModel)
|
||||
if (recognizedData.invoiceDate) formData.value.invoiceDate = String(recognizedData.invoiceDate)
|
||||
if (recognizedData.invoiceAmount != null) formData.value.invoiceAmount = Number(recognizedData.invoiceAmount)
|
||||
if (recognizedData.carModel) formData.value.carModel = String(recognizedData.carModel)
|
||||
// 图片 URL:身份证正反面、购车发票
|
||||
if (recognizedData.idCardFrontUrl) formData.value.idCardFrontUrl = String(recognizedData.idCardFrontUrl)
|
||||
if (recognizedData.idCardBackUrl) formData.value.idCardBackUrl = String(recognizedData.idCardBackUrl)
|
||||
if (recognizedData.carInvoiceUrls && Array.isArray(recognizedData.carInvoiceUrls) && recognizedData.carInvoiceUrls.length > 0) {
|
||||
formData.value.carInvoiceUrls = recognizedData.carInvoiceUrls.map((u: any) => String(u))
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open, openCopy, openWithRecognizedData }) // 提供 open / openCopy / openWithRecognizedData 方法
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
|
||||
@@ -83,6 +83,14 @@
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openRecognizeDialog"
|
||||
v-hasPermi="['car:renewal-order:create']"
|
||||
>
|
||||
<Icon icon="ep:picture" class="mr-5px" /> 图片识别新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@@ -231,6 +239,96 @@
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<RenewalOrderForm ref="formRef" @success="getList" />
|
||||
|
||||
<!-- 图片识别新增:身份证 + 车辆购置发票 -->
|
||||
<el-dialog
|
||||
v-model="recognizeDialogVisible"
|
||||
title="图片识别新增"
|
||||
width="480px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-tabs v-model="recognizeActiveTab">
|
||||
<el-tab-pane label="身份证识别" name="idcard">
|
||||
<div class="flex flex-col gap-16px">
|
||||
<div>
|
||||
<div class="text-sm text-gray-600 mb-8px">身份证正面(人像面)*</div>
|
||||
<el-upload
|
||||
ref="recognizeUploadRef"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:on-change="handleRecognizeFileChange"
|
||||
:on-exceed="() => message.warning('仅支持上传一张图片')"
|
||||
accept="image/jpeg,image/png,image/jpg,image/gif"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload"><Icon icon="ep:upload-filled" /></el-icon>
|
||||
<div class="el-upload__text">将身份证正面拖到此处,或<em>点击选择</em></div>
|
||||
</el-upload>
|
||||
<div v-if="recognizeSelectedFile" class="text-sm text-gray-500 mt-4px">已选择:{{ recognizeSelectedFile.name }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm text-gray-600 mb-8px">身份证反面(国徽面,可选)</div>
|
||||
<el-upload
|
||||
ref="recognizeBackUploadRef"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:on-change="handleRecognizeBackFileChange"
|
||||
:on-exceed="() => message.warning('仅支持上传一张图片')"
|
||||
accept="image/jpeg,image/png,image/jpg,image/gif"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload"><Icon icon="ep:upload-filled" /></el-icon>
|
||||
<div class="el-upload__text">将身份证反面拖到此处,或<em>点击选择</em></div>
|
||||
</el-upload>
|
||||
<div v-if="recognizeBackSelectedFile" class="text-sm text-gray-500 mt-4px">已选择:{{ recognizeBackSelectedFile.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="车辆购置发票识别" name="invoice">
|
||||
<div class="flex flex-col items-center gap-16px">
|
||||
<el-upload
|
||||
ref="invoiceUploadRef"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:on-change="handleInvoiceFileChange"
|
||||
:on-exceed="() => message.warning('仅支持上传一张图片')"
|
||||
accept="image/jpeg,image/png,image/jpg,image/gif"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload"><Icon icon="ep:upload-filled" /></el-icon>
|
||||
<div class="el-upload__text">将机动车销售发票照片拖到此处,或<em>点击选择</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">请上传机动车销售发票照片,支持 jpg/png/gif</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<div v-if="invoiceSelectedFile" class="text-sm text-gray-500">
|
||||
已选择:{{ invoiceSelectedFile.name }}
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<el-button @click="recognizeDialogVisible = false">取消</el-button>
|
||||
<el-button
|
||||
v-if="recognizeActiveTab === 'idcard'"
|
||||
type="primary"
|
||||
:loading="recognizeLoading"
|
||||
:disabled="!recognizeSelectedFile"
|
||||
@click="handleRecognizeSubmit"
|
||||
>
|
||||
识别并新增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
type="primary"
|
||||
:loading="invoiceRecognizeLoading"
|
||||
:disabled="!invoiceSelectedFile"
|
||||
@click="handleInvoiceRecognizeSubmit"
|
||||
>
|
||||
识别并确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 线上签名弹窗:二维码 + 复制链接 -->
|
||||
<el-dialog
|
||||
v-model="onlineSignDialogVisible"
|
||||
@@ -257,6 +355,7 @@ import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
import download from '@/utils/download'
|
||||
import { RenewalOrderApi, RenewalOrderVO } from '@/api/car/renewalorder'
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
import RenewalOrderForm from './RenewalOrderForm.vue'
|
||||
import TireStoreTree from '@/views/tire/tireuser/TireStoreTree.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
@@ -326,6 +425,113 @@ const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 图片识别新增 */
|
||||
const recognizeDialogVisible = ref(false)
|
||||
const recognizeActiveTab = ref<'idcard' | 'invoice'>('idcard')
|
||||
const recognizeUploadRef = ref()
|
||||
const recognizeSelectedFile = ref<File | null>(null)
|
||||
const recognizeBackUploadRef = ref()
|
||||
const recognizeBackSelectedFile = ref<File | null>(null)
|
||||
const recognizeLoading = ref(false)
|
||||
const invoiceUploadRef = ref()
|
||||
const invoiceSelectedFile = ref<File | null>(null)
|
||||
const invoiceRecognizeLoading = ref(false)
|
||||
|
||||
/** 提取识别/上传失败原因,用于提示用户 */
|
||||
const getRecognizeErrorMessage = (e: any, fallback: string): string => {
|
||||
return e?.response?.data?.msg ?? e?.message ?? e?.msg ?? (typeof e === 'string' ? e : fallback)
|
||||
}
|
||||
|
||||
/** 上传文件获取 URL */
|
||||
const uploadFileGetUrl = async (file: File): Promise<string> => {
|
||||
const res = await FileApi.updateFile({ file }) as any
|
||||
return res?.data ?? res?.url ?? ''
|
||||
}
|
||||
|
||||
const openRecognizeDialog = () => {
|
||||
recognizeActiveTab.value = 'idcard'
|
||||
recognizeSelectedFile.value = null
|
||||
recognizeBackSelectedFile.value = null
|
||||
invoiceSelectedFile.value = null
|
||||
recognizeUploadRef.value?.clearFiles()
|
||||
recognizeBackUploadRef.value?.clearFiles()
|
||||
invoiceUploadRef.value?.clearFiles()
|
||||
recognizeDialogVisible.value = true
|
||||
}
|
||||
const handleRecognizeFileChange = (uploadFile: { raw?: File }) => {
|
||||
recognizeSelectedFile.value = uploadFile?.raw || null
|
||||
}
|
||||
const handleRecognizeBackFileChange = (uploadFile: { raw?: File }) => {
|
||||
recognizeBackSelectedFile.value = uploadFile?.raw || null
|
||||
}
|
||||
const handleInvoiceFileChange = (uploadFile: { raw?: File }) => {
|
||||
invoiceSelectedFile.value = uploadFile?.raw || null
|
||||
}
|
||||
const handleRecognizeSubmit = async () => {
|
||||
if (!recognizeSelectedFile.value) return
|
||||
recognizeLoading.value = true
|
||||
try {
|
||||
const res = await RenewalOrderApi.recognizeIdcard(recognizeSelectedFile.value, 'front') as any
|
||||
const data = res?.data ?? res
|
||||
if (!data || typeof data !== 'object') {
|
||||
const errMsg = res?.msg || '识别失败,未获取到有效数据,请重新上传'
|
||||
message.error(errMsg)
|
||||
return
|
||||
}
|
||||
// 上传身份证正反面获取 URL
|
||||
try {
|
||||
const idCardFrontUrl = await uploadFileGetUrl(recognizeSelectedFile.value)
|
||||
if (idCardFrontUrl) data.idCardFrontUrl = idCardFrontUrl
|
||||
if (recognizeBackSelectedFile.value) {
|
||||
const idCardBackUrl = await uploadFileGetUrl(recognizeBackSelectedFile.value)
|
||||
if (idCardBackUrl) data.idCardBackUrl = idCardBackUrl
|
||||
}
|
||||
} catch (uploadErr: any) {
|
||||
const errMsg = getRecognizeErrorMessage(uploadErr, '图片上传失败,请重试')
|
||||
message.error(`图片上传失败:${errMsg}`)
|
||||
return
|
||||
}
|
||||
recognizeDialogVisible.value = false
|
||||
formRef.value.openWithRecognizedData(data)
|
||||
message.success('识别成功,请补充其他信息后保存')
|
||||
} catch (e: any) {
|
||||
const errMsg = getRecognizeErrorMessage(e, '识别失败,请重新上传正确的身份证照片')
|
||||
message.error(errMsg.includes('请重新上传') ? errMsg : `${errMsg},请重新上传`)
|
||||
} finally {
|
||||
recognizeLoading.value = false
|
||||
}
|
||||
}
|
||||
const handleInvoiceRecognizeSubmit = async () => {
|
||||
if (!invoiceSelectedFile.value) return
|
||||
invoiceRecognizeLoading.value = true
|
||||
try {
|
||||
const res = await RenewalOrderApi.recognizeVehicleInvoice(invoiceSelectedFile.value) as any
|
||||
const data = res?.data ?? res
|
||||
if (!data || typeof data !== 'object') {
|
||||
const errMsg = res?.msg || '识别失败,未获取到有效数据,请重新上传'
|
||||
message.error(errMsg)
|
||||
return
|
||||
}
|
||||
// 上传发票图片获取 URL,初始化到购车发票
|
||||
try {
|
||||
const invoiceUrl = await uploadFileGetUrl(invoiceSelectedFile.value)
|
||||
if (invoiceUrl) data.carInvoiceUrls = [invoiceUrl]
|
||||
} catch (uploadErr: any) {
|
||||
const errMsg = getRecognizeErrorMessage(uploadErr, '图片上传失败,请重试')
|
||||
message.error(`图片上传失败:${errMsg}`)
|
||||
return
|
||||
}
|
||||
recognizeDialogVisible.value = false
|
||||
formRef.value.openWithRecognizedData(data)
|
||||
message.success('识别成功,请补充其他信息后保存')
|
||||
} catch (e: any) {
|
||||
const errMsg = getRecognizeErrorMessage(e, '识别失败,请重新上传正确的机动车销售发票照片')
|
||||
message.error(errMsg.includes('请重新上传') ? errMsg : `${errMsg},请重新上传`)
|
||||
} finally {
|
||||
invoiceRecognizeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 复制创建订单:基于原订单预填表单,进入新增模式 */
|
||||
const handleCopy = (id: number) => {
|
||||
formRef.value.openCopy(id)
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
<el-input v-model="formData.code" placeholder="请输入岗位编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位顺序" prop="sort">
|
||||
<el-input v-model="formData.sort" placeholder="请输入岗位顺序" />
|
||||
<el-input-number
|
||||
v-model="formData.sort"
|
||||
:min="0"
|
||||
class="!w-full"
|
||||
controls-position="right"
|
||||
placeholder="请输入岗位顺序"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" clearable placeholder="请选择状态">
|
||||
@@ -61,6 +67,7 @@ const formData = ref({
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '岗位标题不能为空', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '岗位编码不能为空', trigger: 'change' }],
|
||||
sort: [{ required: true, message: '岗位顺序不能为空', trigger: 'change' }],
|
||||
status: [{ required: true, message: '岗位状态不能为空', trigger: 'change' }],
|
||||
remark: [{ required: false, message: '岗位内容不能为空', trigger: 'blur' }]
|
||||
})
|
||||
@@ -116,10 +123,10 @@ const resetForm = () => {
|
||||
id: undefined,
|
||||
name: '',
|
||||
code: '',
|
||||
sort: undefined,
|
||||
sort: 0,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
remark: ''
|
||||
} as any
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<el-form-item label="角色标识">
|
||||
<el-tag>{{ formData.code }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="权限范围">
|
||||
<el-form-item label="部门权限范围">
|
||||
<el-select v-model="formData.dataScope">
|
||||
<el-option
|
||||
v-for="item in getIntDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)"
|
||||
@@ -17,6 +17,36 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="门店权限范围">
|
||||
<el-select v-model="formData.storeDataScope" placeholder="请选择门店数据权限" clearable>
|
||||
<el-option
|
||||
v-for="item in getIntDictOptions(DICT_TYPE.CAR_STORE_DATA_SCOPE)"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="text-gray-400 text-xs mt-1">全部门店/指定门店:查看全部或所选门店;本门店及以下:仅关联门店;仅本人:仅本人创建的数据</div>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.storeDataScope === 4"
|
||||
label="权限范围"
|
||||
style="display: flex"
|
||||
>
|
||||
<el-card class="card" shadow="never">
|
||||
<template #header>
|
||||
指定门店(可多选)
|
||||
</template>
|
||||
<el-tree
|
||||
ref="storeTreeRef"
|
||||
:data="storeOptions"
|
||||
:props="{ label: 'storeName', value: 'id' }"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
empty-text="加载中..."
|
||||
/>
|
||||
</el-card>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item
|
||||
v-if="formData.dataScope === SystemDataScopeEnum.DEPT_CUSTOM"
|
||||
@@ -69,6 +99,7 @@ import { SystemDataScopeEnum } from '@/utils/constants'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
import * as PermissionApi from '@/api/system/permission'
|
||||
import { StoreApi } from '@/api/tire/store'
|
||||
|
||||
defineOptions({ name: 'SystemRoleDataPermissionForm' })
|
||||
|
||||
@@ -82,9 +113,13 @@ const formData = reactive({
|
||||
name: '',
|
||||
code: '',
|
||||
dataScope: undefined,
|
||||
dataScopeDeptIds: []
|
||||
dataScopeDeptIds: [],
|
||||
storeDataScope: undefined as number | undefined,
|
||||
storeDataScopeStoreIds: [] as number[]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const storeTreeRef = ref() // 门店树 Ref
|
||||
const storeOptions = ref<any[]>([]) // 门店列表(用于指定门店)
|
||||
const deptOptions = ref<any[]>([]) // 部门树形结构
|
||||
const deptExpand = ref(true) // 展开/折叠
|
||||
const treeRef = ref() // 菜单树组件 Ref
|
||||
@@ -95,18 +130,36 @@ const checkStrictly = ref(true) // 是否严格模式,即父子不关联
|
||||
const open = async (row: RoleApi.RoleVO) => {
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
formLoading.value = true
|
||||
try {
|
||||
// 加载 Dept 列表。注意,必须放在前面,不然下面 setChecked 没数据节点
|
||||
deptOptions.value = handleTree(await DeptApi.getSimpleDeptList())
|
||||
// 设置数据
|
||||
formData.id = row.id
|
||||
formData.name = row.name
|
||||
formData.code = row.code
|
||||
formData.dataScope = row.dataScope
|
||||
// 加载门店列表(指定门店时使用)
|
||||
const storeList = await StoreApi.findByProductType()
|
||||
storeOptions.value = Array.isArray(storeList) ? storeList : (storeList?.data ?? storeList?.list ?? [])
|
||||
// 从接口拉取最新角色数据,确保 storeDataScope 等字段正确回显
|
||||
const role = await RoleApi.getRole(row.id)
|
||||
formData.id = role.id
|
||||
formData.name = role.name
|
||||
formData.code = role.code
|
||||
formData.dataScope = role.dataScope
|
||||
const ADMIN_ROLES = ['super_admin', 'tenant_admin', 'crm_admin']
|
||||
formData.storeDataScope = role.storeDataScope ?? (ADMIN_ROLES.includes(role.code ?? '') ? 1 : undefined)
|
||||
formData.storeDataScopeStoreIds = role.storeDataScopeStoreIds ? Array.from(role.storeDataScopeStoreIds) : []
|
||||
await nextTick()
|
||||
// 需要在 DOM 渲染完成后,再设置选中状态
|
||||
row.dataScopeDeptIds?.forEach((deptId: number): void => {
|
||||
treeRef.value.setChecked(deptId, true, false)
|
||||
const deptIds = role.dataScopeDeptIds ? Array.from(role.dataScopeDeptIds) : []
|
||||
deptIds.forEach((deptId: number): void => {
|
||||
treeRef.value?.setChecked(deptId, true, false)
|
||||
})
|
||||
// 设置指定门店勾选
|
||||
if (formData.storeDataScope === 4 && formData.storeDataScopeStoreIds?.length) {
|
||||
storeTreeRef.value?.setCheckedKeys(formData.storeDataScopeStoreIds)
|
||||
} else {
|
||||
storeTreeRef.value?.setCheckedKeys([])
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
@@ -121,7 +174,12 @@ const submitForm = async () => {
|
||||
dataScopeDeptIds:
|
||||
formData.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM
|
||||
? []
|
||||
: treeRef.value.getCheckedKeys(false)
|
||||
: treeRef.value.getCheckedKeys(false),
|
||||
storeDataScope: formData.storeDataScope,
|
||||
storeDataScopeStoreIds:
|
||||
formData.storeDataScope === 4
|
||||
? (storeTreeRef.value?.getCheckedKeys(false) ?? formData.storeDataScopeStoreIds ?? [])
|
||||
: []
|
||||
}
|
||||
await PermissionApi.assignRoleDataScope(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
@@ -140,14 +198,17 @@ const resetForm = () => {
|
||||
deptExpand.value = true
|
||||
checkStrictly.value = true
|
||||
// 重置表单
|
||||
formData.value = {
|
||||
Object.assign(formData, {
|
||||
id: undefined,
|
||||
name: '',
|
||||
code: '',
|
||||
dataScope: undefined,
|
||||
dataScopeDeptIds: []
|
||||
}
|
||||
dataScopeDeptIds: [],
|
||||
storeDataScope: undefined,
|
||||
storeDataScopeStoreIds: []
|
||||
})
|
||||
treeRef.value?.setCheckedNodes([])
|
||||
storeTreeRef.value?.setCheckedKeys([])
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
<el-input v-model="formData.code" placeholder="请输入角色标识" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示顺序" prop="sort">
|
||||
<el-input v-model="formData.sort" placeholder="请输入显示顺序" />
|
||||
<el-input-number
|
||||
v-model="formData.sort"
|
||||
:min="0"
|
||||
class="!w-full"
|
||||
controls-position="right"
|
||||
placeholder="请输入显示顺序"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" clearable placeholder="请选择状态">
|
||||
@@ -54,7 +60,7 @@ const formData = ref({
|
||||
id: undefined,
|
||||
name: '',
|
||||
code: '',
|
||||
sort: undefined,
|
||||
sort: 0,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
remark: ''
|
||||
})
|
||||
@@ -90,7 +96,7 @@ const resetForm = () => {
|
||||
id: undefined,
|
||||
name: '',
|
||||
code: '',
|
||||
sort: undefined,
|
||||
sort: 0,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
remark: ''
|
||||
}
|
||||
|
||||
@@ -109,6 +109,26 @@
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['tire:store:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['tire:store:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
@@ -193,12 +213,14 @@ const openForm = (type: string, id?: number) => {
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
const handleDelete = async (row: StoreVO) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await message.delConfirm(
|
||||
row?.storeName ? `是否确认删除门店「${row.storeName}」?` : '是否确认删除该门店?'
|
||||
)
|
||||
// 发起删除
|
||||
await StoreApi.deleteStore(id)
|
||||
await StoreApi.deleteStore(row.id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
|
||||
Reference in New Issue
Block a user