From caad7caa1e3409568ffaf8312064731c9b6147b0 Mon Sep 17 00:00:00 2001
From: MrShi <1878285526@qq.com>
Date: 星期五, 10 四月 2026 20:03:13 +0800
Subject: [PATCH] 门店累表
---
admin/src/components/common/uploadImages.vue | 109 ++++++
admin/src/components/business/OperaShopInfoWindow.vue | 473 ++++++++++++++++++++++++++
admin/src/api/system/common.js | 5
admin/src/api/business/shopInfo.js | 19
admin/src/views/business/storeList.vue | 77 +++
admin/src/components/business/OperaShopEditWindow.vue | 369 ++++++++++++++++++++
6 files changed, 1,036 insertions(+), 16 deletions(-)
diff --git a/admin/src/api/business/shopInfo.js b/admin/src/api/business/shopInfo.js
index a04141c..d394d7f 100644
--- a/admin/src/api/business/shopInfo.js
+++ b/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
}
})
-}
\ No newline at end of file
+}
+
+// 淇敼闂ㄥ簵鐘舵��
+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)
+}
diff --git a/admin/src/api/system/common.js b/admin/src/api/system/common.js
index 1f2087f..eceb493 100644
--- a/admin/src/api/system/common.js
+++ b/admin/src/api/system/common.js
@@ -34,3 +34,8 @@
autoLogin: false
})
}
+
+// 涓婁紶鏂囦欢
+export function upload (data) {
+ return request.post('/web/public/upload', data)
+}
\ No newline at end of file
diff --git a/admin/src/components/business/OperaShopEditWindow.vue b/admin/src/components/business/OperaShopEditWindow.vue
new file mode 100644
index 0000000..f9e942c
--- /dev/null
+++ b/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: '璇疯緭鍏ヨ韩浠借瘉姝i潰', 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>
diff --git a/admin/src/components/business/OperaShopInfoWindow.vue b/admin/src/components/business/OperaShopInfoWindow.vue
new file mode 100644
index 0000000..55322fc
--- /dev/null
+++ b/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>
\ No newline at end of file
diff --git a/admin/src/components/common/uploadImages.vue b/admin/src/components/common/uploadImages.vue
new file mode 100644
index 0000000..c62504e
--- /dev/null
+++ b/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>
\ No newline at end of file
diff --git a/admin/src/views/business/storeList.vue b/admin/src/views/business/storeList.vue
index 10a9a3d..5d5dd4e 100644
--- a/admin/src/views/business/storeList.vue
+++ b/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>
\ No newline at end of file
+</script>
+<style scoped>
+.link-name {
+ color: #2E68EC;
+ text-decoration: underline;
+ cursor: pointer;
+}
+</style>
\ No newline at end of file
--
Gitblit v1.9.3