| admin/src/api/business/shopInfo.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| admin/src/api/system/common.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| admin/src/components/business/OperaShopEditWindow.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| admin/src/components/business/OperaShopInfoWindow.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| admin/src/components/common/uploadImages.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| admin/src/views/business/storeList.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
admin/src/api/business/shopInfo.js
@@ -15,7 +15,7 @@ } export function updateById (data) { return request.post('/business/shopInfo/updateById', data) return request.post('/business/shopInfo/updateShop', data) } export function deleteById (id) { @@ -28,4 +28,19 @@ ids } }) } } // ä¿®æ¹é¨åºç¶æ export function changeStatus (data) { return request.post('/business/shopInfo/changeStatus', data) } // é¨åºè¯¦æ export function detail (id) { return request.get(`/business/shopInfo/detail/${id}`) } // éç½®å¯ç export function resetPassword (data) { return request.post('/business/shopInfo/resetPassword', data) } admin/src/api/system/common.js
@@ -34,3 +34,8 @@ autoLogin: false }) } // ä¸ä¼ æä»¶ export function upload (data) { return request.post('/web/public/upload', data) } admin/src/components/business/OperaShopEditWindow.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,369 @@ <template> <GlobalWindow title="ç¼è¾é¨åº" :visible.sync="visible" width="60%" :confirm-working="isWorking.save" @confirm="handleConfirm" > <el-form ref="form" :model="form" label-width="120px" :rules="rules" class="inline-form"> <div class="form-section"> <h4 class="section-title">è´¦å·ä¿¡æ¯</h4> <el-form-item label="æ³¨åææºå·" prop="telephone"> <el-input v-model="form.telephone" disabled></el-input> </el-form-item> <div class="password-tip">é»è®¤å¯ç é»è®¤ä¸ºï¼ãææºå·åå ä½+@123456"ï¼å¦åå ä½ä¸º981923ï¼é»è®¤å¯ç ï¼981923@123456</div> </div> <div class="form-section"> <h4 class="section-title">åºæ¬ä¿¡æ¯</h4> <el-form-item label="é¨åºåç§°" prop="name"> <el-input v-model="form.name" placeholder="请è¾å ¥é¨åºåç§°"></el-input> </el-form-item> <el-form-item label="é¨åºç±»å" prop="companyType"> <el-radio-group v-model="form.companyType" disabled> <el-radio :label="1">ä¼ä¸</el-radio> <el-radio :label="0">个人</el-radio> </el-radio-group> </el-form-item> <el-form-item label="è系人" prop="linkName"> <el-input v-model="form.linkName" placeholder="请è¾å ¥è系人"></el-input> </el-form-item> <el-form-item label="èç³»çµè¯" prop="linkPhone"> <el-input v-model="form.linkPhone" placeholder="请è¾å ¥èç³»çµè¯"></el-input> </el-form-item> <el-form-item label="身份è¯å·" prop="idcard"> <el-input v-model="form.idcard" placeholder="请è¾å ¥èº«ä»½è¯å·"></el-input> </el-form-item> <el-form-item label="æå¨çå¸åº" prop="areaCode"> <el-cascader v-model="form.areaCode" :options="areaOptions" placeholder="è¯·éæ©çå¸åº"></el-cascader> </el-form-item> <el-form-item label="é¨åºå°å" prop="address"> <el-input v-model="form.address" placeholder="请è¾å ¥é¨åºå°å"></el-input> </el-form-item> <el-form-item label="ç»çº¬åº¦" prop="longitude"> <div class="longitude-latitude"> <el-input v-model="form.longitude" placeholder="ç»åº¦" style="width: 150px;"></el-input> <span class="separator">-</span> <el-input v-model="form.latitude" placeholder="纬度" style="width: 150px;"></el-input> <el-button type="primary" @click="openMapSelector">éæ©</el-button> </div> </el-form-item> </div> <div class="form-section"> <el-tabs v-model="qualificationTab"> <el-tab-pane v-if="form.companyType === 1" label="主ä½èµè´¨ï¼ä¼ä¸ï¼" name="enterprise"> <el-form-item label="æ³äººå§å" prop="legalPersonName"> <el-input v-model="form.legalPersonName" placeholder="请è¾å ¥æ³äººå§å"></el-input> </el-form-item> <el-form-item label="æ³äººææºå·" prop="legalPersonPhone"> <el-input v-model="form.legalPersonPhone" placeholder="请è¾å ¥æ³äººææºå·"></el-input> </el-form-item> <el-form-item label="æ³äººèº«ä»½è¯å·" prop="legalPersonCard"> <el-input v-model="form.legalPersonCard" placeholder="请è¾å ¥æ³äººèº«ä»½è¯å·"></el-input> </el-form-item> <el-form-item label="æ³äººèº«ä»½è¯æ£é¢" prop="idcardImg"> <UploadImages :fileList="form.idcardImg" :uploadData="{ folder: 'shop' }" @getFileList="e => form.idcardImg.push(e)" @deleteRow="index => form.idcardImg.splice(index, 1)" /> </el-form-item> <el-form-item label="æ³äººèº«ä»½è¯åé¢" prop="idcardImgBack"> <UploadImages :fileList="form.idcardImgBack" :uploadData="{ folder: 'shop' }" @getFileList="e => form.idcardImgBack.push(e)" @deleteRow="index => form.idcardImgBack.splice(index, 1)" /> </el-form-item> <el-form-item label="è¥ä¸æ§ç §" prop="businessImg"> <UploadImages :fileList="form.businessImg" :uploadData="{ folder: 'shop' }" @getFileList="e => form.businessImg.push(e)" @deleteRow="index => form.businessImg.splice(index, 1)" /> </el-form-item> </el-tab-pane> <el-tab-pane v-if="form.companyType === 0" label="主ä½èµè´¨ï¼ä¸ªäººï¼" name="personal"> <el-form-item label="èº«ä»½è¯æ£é¢" prop="idcardImg"> <UploadImages :fileList="form.idcardImg" :uploadData="{ folder: 'shop' }" @getFileList="e => form.idcardImg.push(e)" @deleteRow="index => form.idcardImg.splice(index, 1)" /> </el-form-item> <el-form-item label="身份è¯åé¢" prop="idcardImgBack"> <UploadImages :fileList="form.idcardImgBack" :uploadData="{ folder: 'shop' }" @getFileList="e => form.idcardImgBack.push(e)" @deleteRow="index => form.idcardImgBack.splice(index, 1)" /> </el-form-item> <el-form-item label="ææå³å¨åå" prop="laborContractImgs"> <UploadImages :fileList="form.laborContractImgs" :uploadData="{ folder: 'shop' }" :maxCount="3" @getFileList="e => form.laborContractImgs.push(e)" @deleteRow="index => form.laborContractImgs.splice(index, 1)" /> </el-form-item> <el-form-item label="社ä¿ç¼´çº³è¯æ" prop="socialSecurityImgs"> <UploadImages :fileList="form.socialSecurityImgs" :uploadData="{ folder: 'shop' }" :maxCount="3" @getFileList="e => form.socialSecurityImgs.push(e)" @deleteRow="index => form.socialSecurityImgs.splice(index, 1)" /> </el-form-item> </el-tab-pane> <el-tab-pane label="é¨åºç §çåå ¶ä»ææ" name="photos"> <el-form-item label="é¨åºé¨å¤´ç §" prop="storeFrontImgs"> <UploadImages :fileList="form.storeFrontImgs" :uploadData="{ folder: 'shop' }" :maxCount="3" @getFileList="e => form.storeFrontImgs.push(e)" @deleteRow="index => form.storeFrontImgs.splice(index, 1)" /> </el-form-item> <el-form-item label="é¨åºå é¨ç §ç" prop="storeInteriorImgs"> <UploadImages :fileList="form.storeInteriorImgs" :uploadData="{ folder: 'shop' }" :maxCount="3" @getFileList="e => form.storeInteriorImgs.push(e)" @deleteRow="index => form.storeInteriorImgs.splice(index, 1)" /> </el-form-item> <el-form-item label="å ¶å®ææ" prop="otherMaterialImgs"> <UploadImages :fileList="form.otherMaterialImgs" :uploadData="{ folder: 'shop' }" :maxCount="3" @getFileList="e => form.otherMaterialImgs.push(e)" @deleteRow="index => form.otherMaterialImgs.splice(index, 1)" /> </el-form-item> </el-tab-pane> </el-tabs> </div> </el-form> </GlobalWindow> </template> <script> import BaseOpera from '@/components/base/BaseOpera' import GlobalWindow from '@/components/common/GlobalWindow' import UploadImages from '@/components/common/uploadImages' import { detail, updateById } from '@/api/business/shopInfo' import { listByParentId } from '@/api/business/areas' export default { name: 'OperaShopEditWindow', extends: BaseOpera, components: { GlobalWindow, UploadImages }, data () { return { form: { id: null, telephone: '', name: '', companyType: 1, linkName: '', linkPhone: '', idcard: '', areaCode: [], provinceId: '', cityId: '', areaId: '', address: '', longitude: '', latitude: '', legalPersonName: '', legalPersonPhone: '', legalPersonCard: '', idcardImg: '', idcardImgBack: '', businessLicenseImg: '', laborContractImgs: '', socialSecurityImgs: '', storeFrontImgs: '', storeInteriorImgs: '', otherMaterialImgs: '' }, rules: { name: [{ required: true, message: '请è¾å ¥é¨åºåç§°', trigger: 'blur' }], companyType: [{ required: true, message: 'è¯·éæ©é¨åºç±»å', trigger: 'change' }], linkName: [{ required: true, message: '请è¾å ¥è系人', trigger: 'blur' }], linkPhone: [{ required: true, message: '请è¾å ¥èç³»çµè¯', trigger: 'blur' }], idcard: [{ required: true, message: '请è¾å ¥èº«ä»½è¯å·', trigger: 'blur' }], areaCode: [{ required: true, message: 'è¯·éæ©çå¸åº', trigger: 'change' }], address: [{ required: true, message: '请è¾å ¥é¨åºå°å', trigger: 'blur' }], longitude: [{ required: true, message: 'è¯·éæ©ç»çº¬åº¦', trigger: 'blur' }], legalPersonName: [{ required: true, message: '请è¾å ¥æ³äººå§å', trigger: 'blur' }], legalPersonPhone: [{ required: true, message: '请è¾å ¥æ³äººææºå·', trigger: 'blur' }], legalPersonCard: [{ required: true, message: '请è¾å ¥æ³äººèº«ä»½è¯å·', trigger: 'blur' }], businessImg: [{ required: true, message: '请è¾å ¥è¥ä¸æ§ç §', trigger: 'blur' }], idcardImg: [{ required: true, message: '请è¾å ¥èº«ä»½è¯æ£é¢', trigger: 'blur' }], idcardImgBack: [{ required: true, message: '请è¾å ¥èº«ä»½è¯åé¢', trigger: 'blur' }], laborContractImgs: [{ required: true, message: '请è¾å ¥ææå³å¨åå', trigger: 'blur' }], socialSecurityImgs: [{ required: true, message: '请è¾å ¥ç¤¾ä¿ç¼´çº³è¯æ', trigger: 'blur' }], storeFrontImgs: [{ required: true, message: '请è¾å ¥é¨åºé¨å¤´ç §', trigger: 'blur' }], storeInteriorImgs: [{ required: true, message: '请è¾å ¥é¨åºå é¨ç §ç', trigger: 'blur' }], otherMaterialImgs: [{ required: true, message: '请è¾å ¥å ¶å®ææ', trigger: 'blur' }] }, qualificationTab: 'enterprise', areaOptions: [], isWorking: { save: false } } }, created () { this.config({ api: '/business/shopInfo', 'field.id': 'id' }) this.loadAreaOptions() }, methods: { open (title, row) { this.isWorking.save = false detail(row.id) .then(res => { this.form = { id: res.id, telephone: res.telephone || '', name: res.name || '', companyType: res.companyType ?? 1, linkName: res.linkName || '', linkPhone: res.linkPhone || '', idcard: res.idcard || '', areaCode: res.provinceId ? [res.provinceId, res.cityId, res.areaId] : [], provinceId: res.provinceId || '', cityId: res.cityId || '', areaId: res.areaId || '', address: res.address || '', longitude: res.longitude || '', latitude: res.latitude || '', legalPersonName: res.legalPersonName || '', legalPersonPhone: res.legalPersonPhone || '', legalPersonCard: res.legalPersonCard || '', businessImg: res.companyType === 1 ? [{ fileurl: res.businessImg, url: res.imgPrefix + res.businessImg }] : [], idcardImg: [{ fileurl: res.idcardImg, url: res.imgPrefix + res.idcardImg }], idcardImgBack: [{ fileurl: res.idcardImgBack, url: res.imgPrefix + res.idcardImgBack }], laborContractImgs: res.laborContractImgs.map(item => ({ fileurl: item, url: res.imgPrefix + item })), socialSecurityImgs: res.socialSecurityImgs.map(item => ({ fileurl: item, url: res.imgPrefix + item })), storeFrontImgs: res.storeFrontImgs.map(item => ({ fileurl: item, url: res.imgPrefix + item })), storeInteriorImgs: res.storeInteriorImgs.map(item => ({ fileurl: item, url: res.imgPrefix + item })), otherMaterialImgs: res.otherMaterialImgs ? res.otherMaterialImgs.map(item => ({ fileurl: item, url: res.imgPrefix + item })) : [] } this.qualificationTab = res.companyType === 1 ? 'enterprise' : 'personal' this.title = title this.visible = true }) .catch(e => { this.$tip.apiFailed(e) }) }, loadAreaOptions () { listByParentId({ }) .then(data => { this.areaOptions = this.formatAreaData(data) console.log(this.areaOptions) }) .catch(e => { this.$tip.apiFailed(e) }) }, formatAreaData (data) { const map = {} const result = [] data.forEach(item => { map[item.id] = { value: item.id, label: item.name, children: [] } }) data.forEach(item => { if (item.type === 0) { result.push(map[item.id]) } else if (item.type === 1 && item.parentId && map[item.parentId]) { map[item.parentId].children.push(map[item.id]) } else if (item.type === 2 && item.parentId && map[item.parentId]) { map[item.parentId].children.push(map[item.id]) } }) const clearEmptyChildren = (nodes) => { nodes.forEach(node => { if (node.children.length === 0) { node.children = null } else { clearEmptyChildren(node.children) } }) } clearEmptyChildren(result) return result }, openMapSelector () { window.open('https://lbs.qq.com/getPoint/', '_blank') }, handleConfirm () { this.$refs.form.validate(valid => { if (!valid) return this.isWorking.save = true const imageFields = ['businessImg', 'idcardImg', 'idcardImgBack', 'laborContractImgs', 'socialSecurityImgs', 'storeFrontImgs', 'storeInteriorImgs', 'otherMaterialImgs'] const data = { ...this.form } imageFields.forEach(field => { if (data[field]) { const list = Array.isArray(data[field]) ? data[field] : [data[field]] data[field] = list.map(item => typeof item === 'object' ? item.fileurl : item).join(',') } }) data.provinceId = this.form.areaCode[0] || '' data.cityId = this.form.areaCode[1] || '' data.areaId = this.form.areaCode[2] || '' delete data.areaCode updateById(data) .then(res => { this.$tip.apiSuccess(res || 'ä¿åæå') this.visible = false this.$emit('success') }) .catch(e => { this.$tip.apiFailed(e) }) .finally(() => { this.isWorking.save = false }) }) } } } </script> <style scoped> .form-section { margin-bottom: 20px; } .section-title { font-size: 16px; font-weight: bold; color: #303133; margin-bottom: 15px; padding-left: 10px; border-left: 4px solid #2E68EC; } .password-tip { color: #909399; font-size: 12px; margin: -10px 0 15px 120px; } .longitude-latitude { display: flex; align-items: center; gap: 10px; } .longitude-latitude .separator { color: #606266; } .inline-form /deep/ .el-form-item { display: block; margin-right: 0; } </style> admin/src/components/business/OperaShopInfoWindow.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,473 @@ <template> <GlobalWindow :title="title" :visible.sync="visible" width="80%" > <div class="store-header" v-if="storeInfo"> <div class="store-header-left"> <!-- <el-image :src="storeInfo.headImage || defaultAvatar" fit="cover" class="store-avatar"> <div slot="error" class="image-slot"> <i class="el-icon-picture-outline"></i> </div> </el-image> --> </div> <div class="store-header-right"> <div class="store-name">{{ storeInfo.name }}</div> <div class="store-info-row"> <span class="info-item"> <span class="label">é¨åºç±»åï¼</span> <span class="value">{{ storeInfo.companyType === 1 ? 'ä¼ä¸' : '个人' }}</span> </span> <span class="info-item"> <span class="label">è系人ï¼</span> <span class="value">{{ storeInfo.linkName }}</span> </span> <span class="info-item"> <span class="label">èç³»çµè¯ï¼</span> <span class="value">{{ storeInfo.linkPhone }}</span> </span> <span class="info-item"> <span class="label">身份è¯å·ï¼</span> <span class="value">{{ storeInfo.idcard }}</span> </span> </div> </div> </div> <el-tabs v-model="activeTab" class="store-tabs"> <el-tab-pane label="é¨åºä¸ç»©" name="performance"> <el-form ref="searchForm" :model="searchForm" inline> <el-form-item label="交æå·" prop="transactionId"> <el-input v-model="searchForm.transactionId" clearable placeholder="请è¾å ¥äº¤æå·"></el-input> </el-form-item> <el-form-item label="æ¶æ¯ç±»å" prop="type"> <el-select v-model="searchForm.type" clearable placeholder="è¯·éæ©ç±»å"> <el-option label="å ¨é¨" :value="0"></el-option> <el-option label="æ¶å ¥" :value="1"></el-option> <el-option label="æ¯åº" :value="2"></el-option> </el-select> </el-form-item> <el-form-item label="æ¶æ¯æ¶é´" prop="createTime"> <el-date-picker type="daterange" v-model="searchForm.createTime" clearable value-format="yyyy-MM-dd HH:mm:ss" range-separator="-" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" /> </el-form-item> <section> <el-button type="primary" @click="search">æ¥è¯¢</el-button> <el-button @click="reset">éç½®</el-button> <el-button :loading="isWorking.export" @click="exportExcel">导åº</el-button> </section> </el-form> <el-table :data="tableData.list" border stripe v-loading="isWorking.search" class="performance-table"> <el-table-column label="æ¶å ¥/æ¯åº" min-width="100px"> <template slot-scope="{row}"> <span :class="row.type === 1 ? 'income' : 'expense'">{{ row.type === 1 ? 'æ¶å ¥' : 'æ¯åº' }}</span> </template> </el-table-column> <el-table-column prop="amount" label="éé¢ï¼å ï¼" min-width="100px"> <template slot-scope="{row}"> <span :class="row.type === 1 ? 'income' : 'expense'">{{ row.amount }}</span> </template> </el-table-column> <el-table-column prop="createTime" label="æ¶æ¯æ¶é´" min-width="160px"></el-table-column> <el-table-column prop="businessType" label="ä¸å¡ç±»å" min-width="100px"></el-table-column> <el-table-column prop="transactionId" label="交æå·" min-width="180px"></el-table-column> <el-table-column label="ç¶æ" min-width="100px"> <template slot-scope="{row}"> <span :class="row.status === 1 ? 'status-success' : 'status-pending'"> {{ row.status === 1 ? 'å·²å°è´¦' : 'æç°ä¸' }} </span> </template> </el-table-column> </el-table> <pagination @size-change="handleSizeChange" @current-change="handlePageChange" :pagination="tableData.pagination" ></pagination> </el-tab-pane> <el-tab-pane label="é¨åºèµè´¨" name="qualification"> <div class="qualification-content" v-if="storeInfo"> <div class="qualification-section"> <h4 class="section-title">åºæ¬ä¿¡æ¯</h4> <div class="info-grid"> <div class="info-row"> <span class="label">æå¨çå¸åºï¼</span> <span class="value">{{ storeInfo.provinceName || '' }} {{ storeInfo.cityName || '' }} {{ storeInfo.areaName || '' }}</span> </div> <div class="info-row"> <span class="label">é¨åºå°åï¼</span> <span class="value">{{ storeInfo.address }}</span> </div> <div class="info-row"> <span class="label">é¨åºç¶æï¼</span> <span class="value">{{ storeInfo.auditStatus === 0 ? 'å¾ å®¡æ¹' : storeInfo.auditStatus === 1 ? '审æ¹éè¿' : 'å®¡æ¹æªéè¿' }}</span> </div> <div class="info-row"> <span class="label">é éèå´ï¼</span> <span class="value">{{ storeInfo.deliveryRange || 'ææ ' }}</span> </div> </div> </div> <div class="qualification-section"> <h4 class="section-title">主ä½èµè´¨</h4> <template v-if="storeInfo.companyType === 1"> <div class="info-grid"> <div class="info-row"> <span class="label">æ³äººå§åï¼</span> <span class="value">{{ storeInfo.legalPersonName }}</span> </div> <div class="info-row"> <span class="label">æ³äººææºå·ï¼</span> <span class="value">{{ storeInfo.legalPersonPhone }}</span> </div> <div class="info-row"> <span class="label">æ³äººèº«ä»½è¯å·ç ï¼</span> <span class="value">{{ storeInfo.legalPersonCard }}</span> </div> </div> <div class="image-section"> <div class="image-item"> <span class="label">æ³äººèº«ä»½è¯æ£é¢ï¼</span> <el-image :src="storeInfo.idcardImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImg]"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> <div class="image-item"> <span class="label">æ³äººèº«ä»½è¯åé¢ï¼</span> <el-image :src="storeInfo.idcardImgBack" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImgBack]"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> <div class="image-item"> <span class="label">è¥ä¸æ§ç §ï¼</span> <el-image :src="storeInfo.businessImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.businessImg]"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> </div> </template> <template v-else> <div class="image-item-row"> <span class="label">èº«ä»½è¯æ£é¢ï¼</span> <el-image :src="storeInfo.idcardImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImg]"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> <div class="image-item-row"> <span class="label">身份è¯åé¢ï¼</span> <el-image :src="storeInfo.idcardImgBack" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImgBack]"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> <div class="image-item-row"> <span class="label">ææå³å¨ååï¼</span> <div class="image-list"> <el-image v-for="(img, index) in storeInfo.laborContractImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.laborContractImgs"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> </div> <div class="image-item-row"> <span class="label">社ä¿ç¼´çº³è¯æï¼</span> <div class="image-list"> <el-image v-for="(img, index) in storeInfo.socialSecurityImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.socialSecurityImgs"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> </div> </template> </div> <div class="qualification-section" v-if="storeInfo.companyType === 1"> <h4 class="section-title">é¨åºç §çåå ¶ä»ææ</h4> <div class="image-item-row"> <span class="label">é¨åºé¨å¤´ç §ï¼</span> <div class="image-list"> <el-image v-for="(img, index) in storeInfo.storeFrontImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.storeFrontImgs"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> </div> <div class="image-item-row"> <span class="label">é¨åºå é¨ç §çï¼</span> <div class="image-list"> <el-image v-for="(img, index) in storeInfo.storeInteriorImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.storeInteriorImgs"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> </div> <div class="image-item-row"> <span class="label">å ¶å®ææï¼</span> <div class="image-list"> <el-image v-for="(img, index) in storeInfo.otherMaterialImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.otherMaterialImgs"> <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div> </el-image> </div> </div> </div> </div> </el-tab-pane> </el-tabs> </GlobalWindow> </template> <script> import BaseOpera from '@/components/base/BaseOpera' import GlobalWindow from '@/components/common/GlobalWindow' import Pagination from '@/components/common/Pagination' import { detail } from '@/api/business/shopInfo' export default { name: 'OperaShopInfoWindow', extends: BaseOpera, components: { GlobalWindow, Pagination }, data () { return { activeTab: 'performance', storeInfo: null, searchForm: { transactionId: '', type: 0, createTime: '' }, tableData: { list: [], pagination: { pageIndex: 1, pageSize: 10, total: 0 } }, isWorking: { search: false, export: false }, defaultAvatar: 'https://cube.elemecdn.com/3/c7/9d47156420e4e9c6e2c1f6d6e6e6e6e6.jpeg' } }, created () { this.config({ api: '/business/shopInfo', 'field.id': 'id' }) }, methods: { open (title, row) { detail(row.id) .then(res => { this.storeInfo = res this.activeTab = 'performance' this.searchForm = { transactionId: '', type: 0, createTime: '' } this.title = title this.visible = true this.search() }) .catch(e => { this.$tip.apiFailed(e) }) }, search () { this.isWorking.search = true const data = { pageIndex: this.tableData.pagination.pageIndex, pageSize: this.tableData.pagination.pageSize, shopId: this.storeInfo?.id, transactionId: this.searchForm.transactionId, type: this.searchForm.type, startTime: this.searchForm.createTime?.[0] || '', endTime: this.searchForm.createTime?.[1] || '' } this.api.fetchPerformance(data) .then(res => { this.tableData.list = res.list || [] this.tableData.pagination.total = res.total || 0 }) .catch(e => { this.$tip.apiFailed(e) }) .finally(() => { this.isWorking.search = false }) }, reset () { this.searchForm = { transactionId: '', type: 0, createTime: '' } this.search() }, handleSizeChange (pageSize) { this.tableData.pagination.pageSize = pageSize this.search() }, handlePageChange (pageIndex) { this.tableData.pagination.pageIndex = pageIndex this.search() }, exportExcel () { this.isWorking.export = true const data = { shopId: this.storeInfo?.id, transactionId: this.searchForm.transactionId, type: this.searchForm.type, startTime: this.searchForm.createTime?.[0] || '', endTime: this.searchForm.createTime?.[1] || '' } this.api.exportPerformance(data) .then(res => { this.$tip.apiSuccess('å¯¼åºæå') }) .catch(e => { this.$tip.apiFailed(e) }) .finally(() => { this.isWorking.export = false }) } } } </script> <style scoped> .store-header { display: flex; background: #f5f7fa; border-radius: 8px; padding: 20px; margin-bottom: 20px; } .store-header-left { margin-right: 20px; } .store-avatar { width: 80px; height: 80px; border-radius: 50%; } .store-header-right { flex: 1; } .store-name { font-size: 18px; font-weight: bold; color: #303133; margin-bottom: 10px; } .store-info-row { display: flex; flex-wrap: wrap; gap: 20px; } .info-item { font-size: 14px; } .info-item .label { color: #909399; } .info-item .value { color: #606266; } .store-tabs { margin-top: 10px; } .income { color: #67c23a; } .expense { color: #f56c6c; } .status-success { color: #67c23a; } .status-pending { color: #e6a23c; } .performance-table { margin: 15px 0; } .qualification-content { padding: 20px; } .qualification-section { margin-bottom: 30px; } .section-title { font-size: 16px; font-weight: bold; color: #303133; margin-bottom: 15px; padding-left: 10px; border-left: 4px solid #2E68EC; } .info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-bottom: 20px; } .info-row { display: flex; font-size: 14px; } .info-row .label { color: #909399; min-width: 100px; } .info-row .value { color: #606266; } .image-section { display: flex; flex-wrap: wrap; gap: 30px; } .image-item { display: flex; flex-direction: column; } .image-item-row { display: flex; align-items: flex-start; margin-bottom: 20px; } .image-item-row .label { color: #909399; font-size: 14px; min-width: 120px; } .image-list { display: flex; flex-wrap: wrap; gap: 10px; } .image-item .label { color: #909399; font-size: 14px; margin-bottom: 8px; } .qualification-image { width: 150px; height: 100px; border-radius: 4px; border: 1px solid #eee; } .image-slot { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; background: #f5f7fa; color: #909399; font-size: 20px; } </style> admin/src/components/common/uploadImages.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,109 @@ <template> <div class="upload"> <div class="upload-item" v-for="(item, index) in fileList" :key="index + '_' + item.url"> <div class="dele" @click="handleDelete(index)"> <i class="el-icon-delete"></i> </div> <el-image fit="widthFix" :preview-src-list="fileList.map(res => res.url)" :src="fileList[index].url" /> </div> <div class="upload-item" @click="handleClick" v-if="fileList.length < maxCount"> <i class="el-icon-plus"></i> </div> <input type="file" accept="image/*" ref="fileInput" style="opacity: 0; position: fixed; top: -100%; left: -100%;" @change="handleChangeFile"> </div> </template> <script> import { upload } from '@/api/system/common' export default { props: { fileList: { type: Array, default: () => [] }, uploadData: Object, maxCount: { type: Number, default: 1 } }, data() { return { uploadImgUrl: process.env.VUE_APP_API_PREFIX + '/web/public/upload' } }, methods: { handleDelete (index) { this.$emit('deleteRow', index) }, handleClick () { this.$refs.fileInput.click() }, handleChangeFile (e) { let file = e.target.files[0] if (!file) { return } let formData = new FormData() formData.append('file', file) formData.append('folder', this.uploadData.folder) upload(formData) .then(res => { this.$emit('getFileList', { fileurl: res.imgaddr, url: res.url }) }) .catch(e => { this.$tip.apiFailed(e) }) } } } </script> <style lang="scss" scoped> .upload { display: flex; align-items: center; flex-wrap: wrap; .upload-item { width: 90px; height: 90px; display: flex; align-items: center; justify-content: center; flex-direction: column; border: 1px solid #D9D9D9; border-radius: 4px; margin-right: 10px; margin-bottom: 10px; cursor: pointer; position: relative; .dele { position: absolute; top: 0; right: 0; z-index: 10; width: 25px; height: 25px; line-height: 25px; text-align: center; background-color: red; cursor: pointer; .el-icon-delete { font-size: 13px; color: #fff; } } .el-icon-plus { font-size: 24px; color: #909399; } } } </style> admin/src/views/business/storeList.vue
@@ -20,11 +20,11 @@ <el-input v-model="searchForm.linkPhone" clearable placeholder="请è¾å ¥èç³»çµè¯" @keypress.enter.native="search"></el-input> </el-form-item> <el-form-item label="æ³¨åæ¶é´" prop="createTime"> <el-date-picker type="daterange" v-model="searchForm.createTime" clearable value-format="yyyy-MM-dd HH:mm:ss" range-separator="-" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" /> <el-date-picker type="daterange" v-model="searchForm.createTime" clearable value-format="yyyy-MM-dd" range-separator="-" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" @change="handleDateChange" /> </el-form-item> <el-form-item label="ç¶æ" prop="status"> <el-select v-model="searchForm.status" clearable placeholder="è¯·éæ©ç¶æ" @change="search"> <el-form-item label="ç¶æ" prop="auditStatus"> <el-select v-model="searchForm.auditStatus" clearable placeholder="è¯·éæ©ç¶æ" @change="search"> <el-option label="å¾ å®¡æ¹" :value="0"></el-option> <el-option label="审æ¹éè¿" :value="1"></el-option> <el-option label="å®¡æ¹æªéè¿" :value="2"></el-option> @@ -44,21 +44,31 @@ @selection-change="handleSelectionChange" > <el-table-column type="selection" width="55"></el-table-column> <el-table-column prop="name" label="é¨åºåç§°" min-width="120px"></el-table-column> <el-table-column prop="name" label="é¨åºåç§°" min-width="120px"> <template slot-scope="{row}"> <span class="link-name" @click="openShopInfo(row)">{{ row.name }}</span> </template> </el-table-column> <el-table-column prop="companyType" label="ç±»å" min-width="80px"> <template slot-scope="{row}"> {{row.companyType == 1 ? 'ä¼ä¸' : '个人'}} </template> </el-table-column> <el-table-column prop="address" label="é¨åºå°å" min-width="200px"></el-table-column> <el-table-column prop="contact" label="è系人" min-width="100px"></el-table-column> <el-table-column prop="telephone" label="èç³»çµè¯" min-width="120px"></el-table-column> <el-table-column prop="linkName" label="è系人" min-width="100px"></el-table-column> <el-table-column prop="linkPhone" label="èç³»çµè¯" min-width="120px"></el-table-column> <el-table-column prop="createTime" label="æ³¨åæ¥æ" min-width="160px"></el-table-column> <el-table-column label="è´¦å·ç¶æ" min-width="100px"> <template slot-scope="{row}"> <el-switch @change="changeStatus($event, row)" v-model="row.status" active-color="#13ce66" inactive-color="#ff4949" :active-value="0" :inactive-value="1"> </el-switch> </template> </el-table-column> <el-table-column label="æä½" min-width="150" fixed="right"> <template slot-scope="{row}"> <el-button type="text" @click="handleEdit(row)">ç¼è¾</el-button> <el-button type="text" @click="handleResetPwd(row)">éç½®å¯ç </el-button> </template> </el-table-column> </el-table> @@ -69,6 +79,10 @@ > </pagination> </template> <!-- é¨åºè¯¦æ --> <OperaShopInfoWindow ref="operaShopInfoWindow" /> <!-- ç¼è¾é¨åº --> <OperaShopEditWindow ref="operaShopEditWindow" @success="search" /> </TableLayout> </template> @@ -76,10 +90,13 @@ import BaseTable from '@/components/base/BaseTable' import TableLayout from '@/layouts/TableLayout' import Pagination from '@/components/common/Pagination' import OperaShopInfoWindow from '@/components/business/OperaShopInfoWindow' import OperaShopEditWindow from '@/components/business/OperaShopEditWindow' import { changeStatus, resetPassword } from '@/api/business/shopInfo' export default { name: 'StoreList', extends: BaseTable, components: { TableLayout, Pagination }, components: { TableLayout, Pagination, OperaShopInfoWindow, OperaShopEditWindow }, data () { return { searchForm: { @@ -89,7 +106,9 @@ linkName: '', linkPhone: '', createTime: '', status: '' createStartTime: '', createEndTime: '', auditStatus: '' } } }, @@ -106,18 +125,22 @@ reset () { this.searchForm = { name: '', type: '', companyType: '', address: '', contact: '', telephone: '', linkName: '', linkPhone: '', createTime: '', status: '' } this.search() }, handleDateChange (val) { this.searchForm.createStartTime = val ? val[0] : '' this.searchForm.createEndTime = val ? val[1] : '' }, changeStatus (e, row) { this.working = true this.api.updateStatus({ id: row.id, status: e }) changeStatus({ id: row.id, status: e }) .then(res => { this.$tip.apiSuccess(res || 'æä½æå') this.search() @@ -128,7 +151,33 @@ .finally(() => { this.working = false }) }, openShopInfo (row) { this.$refs.operaShopInfoWindow.open('é¨åºä¿¡æ¯', row) }, handleEdit (row) { this.$refs.operaShopEditWindow.open('ç¼è¾é¨åº', row) }, handleResetPwd (row) { this.$confirm('æ¯å¦ç¡®è®¤éç½®é¨åºå¯ç ï¼', 'æç¤º') .then(() => { resetPassword({ id: row.id }) .then(res => { this.$tip.apiSuccess(res || 'éç½®å¯ç æå') }) .catch(e => { this.$tip.apiFailed(e) }) }) .catch(() => {}) } } } </script> </script> <style scoped> .link-name { color: #2E68EC; text-decoration: underline; cursor: pointer; } </style>