第一次提交:Vue3 后台前端项目当前最新版
This commit is contained in:
578
src/views/shop/List.vue
Normal file
578
src/views/shop/List.vue
Normal file
@@ -0,0 +1,578 @@
|
||||
<template>
|
||||
<div class="shop-list-page">
|
||||
<!-- 默认配置区域 -->
|
||||
<el-card class="default-config-card" shadow="never">
|
||||
<template #header>
|
||||
<span class="card-title">系统默认配置</span>
|
||||
</template>
|
||||
<div class="default-config-content">
|
||||
<div class="config-item">
|
||||
<span class="config-label">默认订单抽成费率:</span>
|
||||
<span class="config-value">{{ formatPercent(defaultConfig.moren_choucheng_feilv) }}</span>
|
||||
</div>
|
||||
<div class="config-item">
|
||||
<span class="config-label">默认可提现限额:</span>
|
||||
<span class="config-value">¥{{ defaultConfig.moren_tixian_xiane }}</span>
|
||||
</div>
|
||||
<el-button type="primary" size="small" @click="openDefaultConfigDialog">修改默认配置</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 筛选区域 -->
|
||||
<el-card class="filter-card" shadow="never">
|
||||
<el-form :inline="true" :model="filterForm" class="filter-form">
|
||||
<el-form-item label="店铺名称">
|
||||
<el-input v-model="filterForm.dianpu_mingcheng" placeholder="输入名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺状态">
|
||||
<el-select v-model="filterForm.zhuangtai" placeholder="全部" clearable>
|
||||
<el-option label="正常" :value="1" />
|
||||
<el-option label="封禁" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="filterForm.lianxi_dianhua" placeholder="输入电话" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户ID">
|
||||
<el-input v-model="filterForm.user_id" placeholder="输入用户ID" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="提现余额">
|
||||
<el-input-number v-model="filterForm.ketixian_yue_min" :min="0" placeholder="≥" style="width: 120px" />
|
||||
<span style="margin: 0 8px">—</span>
|
||||
<el-input-number v-model="filterForm.ketixian_yue_max" :min="0" placeholder="≤" style="width: 120px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="赚取总额">
|
||||
<el-input-number v-model="filterForm.zhengqu_zonge_min" :min="0" placeholder="≥" style="width: 120px" />
|
||||
<span style="margin: 0 8px">—</span>
|
||||
<el-input-number v-model="filterForm.zhengqu_zonge_max" :min="0" placeholder="≤" style="width: 120px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="resetFilter">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 列表表格 -->
|
||||
<el-card class="table-card" shadow="never">
|
||||
<div class="table-header-actions">
|
||||
<el-button type="success" @click="openAddDialog">添加店铺</el-button>
|
||||
</div>
|
||||
<el-table :data="shopList" v-loading="loading" stripe border style="width: 100%">
|
||||
<el-table-column label="头像" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-avatar :size="40" :src="getFullAvatar(row.dianpu_touxiang)">
|
||||
<el-icon :size="24"><Shop /></el-icon>
|
||||
</el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dianpu_mingcheng" label="店铺名称" min-width="120" />
|
||||
<el-table-column prop="id" label="店铺ID" width="80" />
|
||||
<el-table-column prop="pingzheng__yonghu" label="用户ID" width="100" />
|
||||
<el-table-column label="可提现余额" width="130">
|
||||
<template #default="{ row }">¥{{ row.ketixian_yue }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="赚取总额" width="130">
|
||||
<template #default="{ row }">¥{{ row.zhengqu_zonge }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.zhuangtai === 1 ? 'success' : 'danger'" size="small">
|
||||
{{ row.zhuangtai === 1 ? '正常' : '封禁' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" @click="openDetailDialog(row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.page"
|
||||
:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
layout="total, prev, pager, next, jumper"
|
||||
@current-change="fetchShopList"
|
||||
style="margin-top: 20px; justify-content: flex-end;"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 详情/编辑弹窗 -->
|
||||
<el-dialog v-model="detailVisible" title="店铺详情" width="700px" destroy-on-close>
|
||||
<template v-if="detailForm">
|
||||
<el-form :model="detailForm" label-width="140px" :disabled="!isEditing">
|
||||
<el-form-item label="店铺ID">
|
||||
<el-input v-model="detailForm.id" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户ID">
|
||||
<el-input :value="detailForm.pingzheng__yonghu || ''" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺名称">
|
||||
<el-input v-model="detailForm.dianpu_mingcheng" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="detailForm.lianxi_dianhua" />
|
||||
</el-form-item>
|
||||
<el-form-item label="微信号">
|
||||
<el-input v-model="detailForm.weixinhao" />
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺状态">
|
||||
<el-switch
|
||||
v-model="detailForm.zhuangtai"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-text="正常"
|
||||
inactive-text="封禁"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="可提现余额">
|
||||
<el-input-number v-model="detailForm.ketixian_yue" :min="0" :precision="2" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启收益抽成">
|
||||
<el-switch v-model="detailForm.kaiqi_shouyi_choucheng" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="detailForm.kaiqi_shouyi_choucheng" label="抽成费率(%)">
|
||||
<el-input-number
|
||||
v-model="detailForm.shouyi_choucheng_feilv_percent"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="2"
|
||||
placeholder="例如 10 表示 10%"
|
||||
/>
|
||||
<span class="form-tip">输入百分比数值,如 10 表示 10%</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启每日提现限额">
|
||||
<el-switch v-model="detailForm.kaiqi_meiri_xiane" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="detailForm.kaiqi_meiri_xiane" label="每日提现限额">
|
||||
<el-input-number v-model="detailForm.meiri_tixian_xiane" :min="0" :precision="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="detail-actions">
|
||||
<el-button v-if="!isEditing" type="warning" @click="enterEdit">编辑</el-button>
|
||||
<template v-else>
|
||||
<el-button type="primary" @click="saveDetail">保存</el-button>
|
||||
<el-button @click="cancelEdit">取消</el-button>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="operation-data-link">
|
||||
<el-button type="info" plain @click="openOperationDataDialog(detailForm.id)">
|
||||
查看店铺运营数据
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加店铺弹窗 -->
|
||||
<el-dialog v-model="addVisible" title="添加店铺" width="600px" destroy-on-close>
|
||||
<el-form :model="addForm" label-width="160px">
|
||||
<el-form-item label="用户ID" required>
|
||||
<el-input v-model="addForm.yonghu_id" placeholder="7位用户ID" maxlength="7" />
|
||||
</el-form-item>
|
||||
<el-form-item label="登录账号" required>
|
||||
<el-input v-model="addForm.zhanghao" placeholder="账号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="登录密码" required>
|
||||
<el-input v-model="addForm.mima" placeholder="密码" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺名称" required>
|
||||
<el-input v-model="addForm.dianpu_mingcheng" placeholder="店铺名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="addForm.lianxi_dianhua" placeholder="选填" />
|
||||
</el-form-item>
|
||||
<el-form-item label="微信号">
|
||||
<el-input v-model="addForm.weixinhao" placeholder="选填" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启收益抽成">
|
||||
<el-switch v-model="addForm.kaiqi_shouyi_choucheng" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="addForm.kaiqi_shouyi_choucheng" label="抽成费率(%)">
|
||||
<el-input-number v-model="addForm.shouyi_choucheng_feilv_percent" :min="0" :max="100" :precision="2" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启每日提现限额">
|
||||
<el-switch v-model="addForm.kaiqi_meiri_xiane" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="addForm.kaiqi_meiri_xiane" label="每日提现限额">
|
||||
<el-input-number v-model="addForm.meiri_tixian_xiane" :min="0" :precision="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="addVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitAdd">确定添加</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 默认配置修改弹窗 -->
|
||||
<el-dialog v-model="defaultConfigVisible" title="修改默认配置" width="500px">
|
||||
<el-form label-width="160px">
|
||||
<el-form-item label="默认抽成费率(%)">
|
||||
<el-input-number
|
||||
v-model="defaultEditForm.moren_choucheng_feilv_percent"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="默认可提现限额">
|
||||
<el-input-number v-model="defaultEditForm.moren_tixian_xiane" :min="0" :precision="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="defaultConfigVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveDefaultConfig">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 运营数据弹窗(内嵌子组件) -->
|
||||
<el-dialog v-model="operationVisible" title="店铺运营数据" width="80%" top="5vh" destroy-on-close>
|
||||
<ShopOperationData v-if="operationDianpuId" :dianpu-id="operationDianpuId" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Shop } from '@element-plus/icons-vue'
|
||||
import request from '@/utils/request'
|
||||
import ShopOperationData from './components/ShopOperationData.vue' // 引入子组件
|
||||
|
||||
// ==================== 默认配置相关 ====================
|
||||
const defaultConfig = reactive({
|
||||
moren_choucheng_feilv: 0,
|
||||
moren_tixian_xiane: 0
|
||||
})
|
||||
|
||||
// ==================== 筛选 ====================
|
||||
const filterForm = reactive({
|
||||
dianpu_mingcheng: '',
|
||||
zhuangtai: null, // 1正常 0封禁 全部为null
|
||||
lianxi_dianhua: '',
|
||||
user_id: '',
|
||||
ketixian_yue_min: null,
|
||||
ketixian_yue_max: null,
|
||||
zhengqu_zonge_min: null,
|
||||
zhengqu_zonge_max: null
|
||||
})
|
||||
|
||||
const shopList = ref([])
|
||||
const loading = ref(false)
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 工具:前端费率百分比转换(接口内部存储为小数,如0.1表示10%)
|
||||
const toPercent = (decimal) => {
|
||||
if (decimal == null) return 0
|
||||
return Number((decimal * 100).toFixed(2))
|
||||
}
|
||||
const toDecimal = (percent) => {
|
||||
if (percent == null) return 0
|
||||
return Number((percent / 100).toFixed(4))
|
||||
}
|
||||
const formatPercent = (decimal) => {
|
||||
return toPercent(decimal) + '%'
|
||||
}
|
||||
|
||||
// 头像拼接
|
||||
const getFullAvatar = (url) => {
|
||||
if (!url) return ''
|
||||
if (url.startsWith('http')) return url
|
||||
const base = window.$ossURL || ''
|
||||
return base.replace(/\/$/, '') + '/' + url.replace(/^\//, '')
|
||||
}
|
||||
|
||||
// 获取店铺列表
|
||||
const fetchShopList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {
|
||||
page: pagination.page,
|
||||
page_size: pagination.pageSize,
|
||||
username: localStorage.getItem('username') || '',
|
||||
...filterForm
|
||||
}
|
||||
const res = await request({
|
||||
url: '/houtai/hqdplbsj',
|
||||
method: 'POST', // 假设为POST,根据实际调整
|
||||
data: params
|
||||
})
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
const data = res.data
|
||||
shopList.value = data.list || []
|
||||
pagination.total = data.total || 0
|
||||
// 默认配置可能随列表一起返回
|
||||
if (data.default_config) {
|
||||
Object.assign(defaultConfig, data.default_config)
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取店铺列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error)
|
||||
ElMessage.error('网络错误')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.page = 1
|
||||
fetchShopList()
|
||||
}
|
||||
const resetFilter = () => {
|
||||
Object.keys(filterForm).forEach(key => (filterForm[key] = key.includes('min') || key.includes('max') ? null : ''))
|
||||
filterForm.zhuangtai = null
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
// ==================== 详情编辑弹窗 ====================
|
||||
const detailVisible = ref(false)
|
||||
const isEditing = ref(false)
|
||||
const detailForm = ref(null)
|
||||
|
||||
const openDetailDialog = (row) => {
|
||||
// 深拷贝,并计算百分比展示
|
||||
detailForm.value = {
|
||||
...row,
|
||||
shouyi_choucheng_feilv_percent: toPercent(row.shouyi_choucheng_feilv),
|
||||
// 补全可能缺失的开关
|
||||
kaiqi_shouyi_choucheng: row.kaiqi_shouyi_choucheng || false,
|
||||
kaiqi_meiri_xiane: row.kaiqi_meiri_xiane || false,
|
||||
}
|
||||
isEditing.value = false
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
const enterEdit = () => {
|
||||
isEditing.value = true
|
||||
}
|
||||
const cancelEdit = () => {
|
||||
isEditing.value = false
|
||||
// 恢复到原始数据
|
||||
const original = shopList.value.find(item => item.id === detailForm.value.id)
|
||||
if (original) {
|
||||
detailForm.value = {
|
||||
...original,
|
||||
shouyi_choucheng_feilv_percent: toPercent(original.shouyi_choucheng_feilv),
|
||||
kaiqi_shouyi_choucheng: original.kaiqi_shouyi_choucheng || false,
|
||||
kaiqi_meiri_xiane: original.kaiqi_meiri_xiane || false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const saveDetail = async () => {
|
||||
const form = detailForm.value
|
||||
if (!form) return
|
||||
// 转换抽成费率
|
||||
const feilv = toDecimal(form.shouyi_choucheng_feilv_percent)
|
||||
const data = {
|
||||
action: 'update_dianpu', // 明确操作类型
|
||||
dianpu_id: form.id,
|
||||
username: localStorage.getItem('username') || '',
|
||||
dianpu_mingcheng: form.dianpu_mingcheng,
|
||||
lianxi_dianhua: form.lianxi_dianhua,
|
||||
weixinhao: form.weixinhao,
|
||||
zhuangtai: form.zhuangtai,
|
||||
ketixian_yue: form.ketixian_yue,
|
||||
kaiqi_shouyi_choucheng: form.kaiqi_shouyi_choucheng,
|
||||
shouyi_choucheng_feilv: feilv,
|
||||
kaiqi_meiri_xiane: form.kaiqi_meiri_xiane,
|
||||
meiri_tixian_xiane: form.meiri_tixian_xiane || 0
|
||||
}
|
||||
try {
|
||||
const res = await request.post('/houtai/xgdpxx', data)
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
ElMessage.success('修改成功')
|
||||
detailVisible.value = false
|
||||
fetchShopList() // 刷新列表
|
||||
} else {
|
||||
ElMessage.error(res.msg || '修改失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('请求异常')
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 添加店铺弹窗 ====================
|
||||
const addVisible = ref(false)
|
||||
const addForm = reactive({
|
||||
yonghu_id: '',
|
||||
zhanghao: '',
|
||||
mima: '',
|
||||
dianpu_mingcheng: '',
|
||||
lianxi_dianhua: '',
|
||||
weixinhao: '',
|
||||
kaiqi_shouyi_choucheng: false,
|
||||
shouyi_choucheng_feilv_percent: 0,
|
||||
kaiqi_meiri_xiane: false,
|
||||
meiri_tixian_xiane: 0
|
||||
})
|
||||
|
||||
const openAddDialog = () => {
|
||||
Object.assign(addForm, {
|
||||
yonghu_id: '',
|
||||
zhanghao: '',
|
||||
mima: '',
|
||||
dianpu_mingcheng: '',
|
||||
lianxi_dianhua: '',
|
||||
weixinhao: '',
|
||||
kaiqi_shouyi_choucheng: false,
|
||||
shouyi_choucheng_feilv_percent: 0,
|
||||
kaiqi_meiri_xiane: false,
|
||||
meiri_tixian_xiane: 0
|
||||
})
|
||||
addVisible.value = true
|
||||
}
|
||||
|
||||
const submitAdd = async () => {
|
||||
if (!addForm.yonghu_id || !addForm.zhanghao || !addForm.mima || !addForm.dianpu_mingcheng) {
|
||||
ElMessage.warning('请填写必填项(用户ID、账号、密码、店铺名称)')
|
||||
return
|
||||
}
|
||||
const feilv = toDecimal(addForm.shouyi_choucheng_feilv_percent)
|
||||
const data = {
|
||||
action: 'add_dianpu',
|
||||
username: localStorage.getItem('username') || '',
|
||||
yonghu_id: addForm.yonghu_id,
|
||||
zhanghao: addForm.zhanghao,
|
||||
mima: addForm.mima,
|
||||
dianpu_mingcheng: addForm.dianpu_mingcheng,
|
||||
lianxi_dianhua: addForm.lianxi_dianhua,
|
||||
weixinhao: addForm.weixinhao,
|
||||
kaiqi_shouyi_choucheng: addForm.kaiqi_shouyi_choucheng,
|
||||
shouyi_choucheng_feilv: feilv,
|
||||
kaiqi_meiri_xiane: addForm.kaiqi_meiri_xiane,
|
||||
meiri_tixian_xiane: addForm.meiri_tixian_xiane
|
||||
}
|
||||
try {
|
||||
const res = await request.post('/houtai/xgdpxx', data)
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
ElMessage.success('添加成功')
|
||||
addVisible.value = false
|
||||
fetchShopList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '添加失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('请求异常')
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 默认配置修改 ====================
|
||||
const defaultConfigVisible = ref(false)
|
||||
const defaultEditForm = reactive({
|
||||
moren_choucheng_feilv_percent: 0,
|
||||
moren_tixian_xiane: 0
|
||||
})
|
||||
|
||||
const openDefaultConfigDialog = () => {
|
||||
defaultEditForm.moren_choucheng_feilv_percent = toPercent(defaultConfig.moren_choucheng_feilv)
|
||||
defaultEditForm.moren_tixian_xiane = defaultConfig.moren_tixian_xiane
|
||||
defaultConfigVisible.value = true
|
||||
}
|
||||
|
||||
const saveDefaultConfig = async () => {
|
||||
const feilv = toDecimal(defaultEditForm.moren_choucheng_feilv_percent)
|
||||
try {
|
||||
const res = await request.post('/houtai/xgdpxx', {
|
||||
action: 'update_default',
|
||||
username: localStorage.getItem('username') || '',
|
||||
moren_choucheng_feilv: feilv,
|
||||
moren_tixian_xiane: defaultEditForm.moren_tixian_xiane
|
||||
})
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
ElMessage.success('默认配置已更新')
|
||||
defaultConfig.moren_choucheng_feilv = feilv
|
||||
defaultConfig.moren_tixian_xiane = defaultEditForm.moren_tixian_xiane
|
||||
defaultConfigVisible.value = false
|
||||
} else {
|
||||
ElMessage.error(res.msg || '更新失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('请求异常')
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 运营数据弹窗 ====================
|
||||
const operationVisible = ref(false)
|
||||
const operationDianpuId = ref(null)
|
||||
|
||||
const openOperationDataDialog = (dianpuId) => {
|
||||
operationDianpuId.value = dianpuId
|
||||
operationVisible.value = true
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
fetchShopList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.shop-list-page {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.default-config-card,
|
||||
.filter-card,
|
||||
.table-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.default-config-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 40px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.config-label {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.config-value {
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.filter-form .el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.table-header-actions {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-actions {
|
||||
margin-top: 24px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.operation-data-link {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
margin-left: 8px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
498
src/views/shop/Products.vue
Normal file
498
src/views/shop/Products.vue
Normal file
@@ -0,0 +1,498 @@
|
||||
<template>
|
||||
<div class="shop-product-manage">
|
||||
<!-- 审核模式开关 -->
|
||||
<el-card shadow="never" class="top-card">
|
||||
<div class="switch-bar">
|
||||
<span class="switch-label">商品审核模式:</span>
|
||||
<el-switch
|
||||
v-model="auditMode"
|
||||
active-text="开启"
|
||||
inactive-text="关闭"
|
||||
:before-change="beforeSwitchAudit"
|
||||
/>
|
||||
<span class="switch-desc">开启后,商家发布的商品需审核才能上架</span>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 第一区域:公共商品类型(横排) -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<template #header>
|
||||
<span class="card-title">商品类型</span>
|
||||
</template>
|
||||
<div class="type-tabs">
|
||||
<el-button
|
||||
v-for="type in publicTypes"
|
||||
:key="type.id"
|
||||
:type="selectedPublicType === type.id ? 'primary' : ''"
|
||||
size="small"
|
||||
@click="selectPublicType(type.id)"
|
||||
>
|
||||
<el-image
|
||||
:src="getFullUrl(type.tupian_url)"
|
||||
style="width:20px;height:20px;vertical-align:middle;margin-right:4px"
|
||||
fit="contain"
|
||||
>
|
||||
<template #error><span></span></template>
|
||||
</el-image>
|
||||
{{ type.jieshao || '未命名' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 第二区域:店铺列表(分页、搜索、筛选) -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<template #header>
|
||||
<span class="card-title">所属店铺</span>
|
||||
</template>
|
||||
<div class="search-bar">
|
||||
<el-input
|
||||
v-model="shopSearch.key"
|
||||
placeholder="店铺ID/用户ID/名称"
|
||||
clearable
|
||||
@clear="shopSearch.key=''"
|
||||
@keyup.enter="searchShops"
|
||||
style="width:220px"
|
||||
/>
|
||||
<el-select v-model="shopSearch.status" placeholder="状态" clearable style="width:120px">
|
||||
<el-option label="正常" :value="1" />
|
||||
<el-option label="封禁" :value="0" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="searchShops">查询</el-button>
|
||||
</div>
|
||||
<div class="shop-list">
|
||||
<div
|
||||
v-for="shop in shopList"
|
||||
:key="shop.id"
|
||||
class="shop-item"
|
||||
:class="{ active: selectedShopId === shop.id }"
|
||||
@click="selectShop(shop.id)"
|
||||
>
|
||||
<div>{{ shop.dianpu_mingcheng }}</div>
|
||||
<div class="sub">ID:{{ shop.id }} 用户:{{ shop.yonghu_id }}</div>
|
||||
<el-tag :type="shop.zhuangtai === 1 ? 'success' : 'danger'" size="small">
|
||||
{{ shop.zhuangtai === 1 ? '正常' : '封禁' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<el-pagination
|
||||
v-model:current-page="shopPage.page"
|
||||
:page-size="shopPage.size"
|
||||
:total="shopPage.total"
|
||||
layout="prev, pager, next"
|
||||
@current-change="loadShopList"
|
||||
small
|
||||
style="margin-top:12px"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 第三区域:店铺商品类型映射(分页、搜索、筛选、待审数标记) -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<template #header>
|
||||
<span class="card-title">店铺商品类型</span>
|
||||
<el-badge :value="pendingTotal" class="pending-badge" type="danger" />
|
||||
</template>
|
||||
<div class="search-bar">
|
||||
<el-input
|
||||
v-model="mappingSearch.keyword"
|
||||
placeholder="类型介绍/ID/店铺"
|
||||
clearable
|
||||
style="width:200px"
|
||||
@keyup.enter="searchMappings"
|
||||
/>
|
||||
<el-select v-model="mappingSearch.shangjia" placeholder="上架状态" clearable style="width:100px">
|
||||
<el-option label="已上架" :value="true" />
|
||||
<el-option label="未上架" :value="false" />
|
||||
</el-select>
|
||||
<el-select v-model="mappingSearch.fengjin" placeholder="封禁状态" clearable style="width:100px">
|
||||
<el-option label="未封禁" :value="false" />
|
||||
<el-option label="已封禁" :value="true" />
|
||||
</el-select>
|
||||
<el-select v-model="mappingSearch.shenhe" placeholder="审核状态" clearable style="width:100px">
|
||||
<el-option label="已通过" :value="true" />
|
||||
<el-option label="未审核" :value="false" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="searchMappings">查询</el-button>
|
||||
</div>
|
||||
<el-table :data="mappingList" v-loading="mappingLoading" stripe border>
|
||||
<el-table-column prop="id" label="ID" width="70" />
|
||||
<el-table-column label="类型图片" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="getFullUrl(row.tupian_url)" fit="contain" style="width:40px;height:40px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="jieshao" label="类型介绍" width="120" />
|
||||
<el-table-column prop="dianpu_name" label="所属店铺" width="100" />
|
||||
<el-table-column prop="gonggong_leixing_name" label="映射公共类型" width="110" />
|
||||
<el-table-column label="上架" width="70">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.shangjia_zhuangtai ? 'success' : 'info'" size="small">
|
||||
{{ row.shangjia_zhuangtai ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="封禁" width="70">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.fengjin_zhuangtai ? 'danger' : 'success'" size="small">
|
||||
{{ row.fengjin_zhuangtai ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" width="80">
|
||||
<template #default="{ row }">
|
||||
<span :style="{ color: row.shenhe_zhuangtai ? '#67c23a' : '#f56c6c' }">
|
||||
{{ row.shenhe_zhuangtai ? '已通过' : '待审核' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" @click="openDetailDialog(row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-model:current-page="mappingPage.page"
|
||||
:page-size="mappingPage.size"
|
||||
:total="mappingPage.total"
|
||||
layout="total, prev, pager, next"
|
||||
@current-change="loadMappings"
|
||||
style="margin-top:12px"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 第四区域:商品列表(子组件) -->
|
||||
<ShopProductList
|
||||
:shop-id="selectedShopId"
|
||||
:shop-product-type-id="null"
|
||||
:public-type-id="selectedPublicType"
|
||||
:public-types="publicTypes"
|
||||
@detail="openProductDetail"
|
||||
/>
|
||||
|
||||
<!-- 详情弹窗(修改映射) -->
|
||||
<el-dialog v-model="detailDialog" title="店铺商品类型详情" width="600px" destroy-on-close>
|
||||
<el-form v-if="editingMapping" label-width="120px">
|
||||
<el-form-item label="类型介绍">{{ editingMapping.jieshao }}</el-form-item>
|
||||
<el-form-item label="上架状态">
|
||||
<el-switch
|
||||
v-model="editingMapping.shangjia_zhuangtai"
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="封禁状态">
|
||||
<el-switch
|
||||
v-model="editingMapping.fengjin_zhuangtai"
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核状态">
|
||||
<el-switch
|
||||
v-model="editingMapping.shenhe_zhuangtai"
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
active-text="通过"
|
||||
inactive-text="待审核"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="detailDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSaveMapping">保存修改</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '@/utils/request'
|
||||
import ShopProductList from './components/ShopProductList.vue'
|
||||
|
||||
// ========== 审核模式 ==========
|
||||
const auditMode = ref(false)
|
||||
|
||||
// 开关前置确认
|
||||
const beforeSwitchAudit = () => {
|
||||
return new Promise((resolve) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要${auditMode.value ? '关闭' : '开启'}商品审核模式吗?`,
|
||||
'提示',
|
||||
{ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||
)
|
||||
.then(() => {
|
||||
// 用户确认,先保持开关状态(由 v-model 控制),然后发送请求
|
||||
// 这里不能直接用 resolve(true),因为需要等待异步请求完成才能决定是否允许切换
|
||||
// 使用 before-change 的标准流程:先发请求,成功则 resolve(true),失败 reject
|
||||
handleSwitchAudit()
|
||||
.then(() => resolve(true))
|
||||
.catch(() => resolve(false)) // 失败时阻止切换
|
||||
})
|
||||
.catch(() => {
|
||||
// 取消操作,阻止切换
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleSwitchAudit = async () => {
|
||||
const targetValue = !auditMode.value
|
||||
try {
|
||||
const res = await request.post('/houtai/htxgdpsplx', {
|
||||
action: 'update_audit_mode',
|
||||
username: localStorage.getItem('username') || '',
|
||||
kaiqi_shenhe: targetValue
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('审核模式已更新')
|
||||
// 请求成功后 v-model 会自动切换到 targetValue,因为 before-change 返回 true 时执行切换
|
||||
} else {
|
||||
ElMessage.error(res.msg || '更新失败')
|
||||
throw new Error('更新失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('请求失败,未修改')
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 公共商品类型 ==========
|
||||
const publicTypes = ref([])
|
||||
const selectedPublicType = ref(null)
|
||||
|
||||
const getFullUrl = (url) => {
|
||||
if (!url) return ''
|
||||
if (url.startsWith('http')) return url
|
||||
const base = window.$ossURL || ''
|
||||
return base.replace(/\/$/, '') + '/' + url.replace(/^\//, '')
|
||||
}
|
||||
|
||||
const selectPublicType = (id) => {
|
||||
selectedPublicType.value = selectedPublicType.value === id ? null : id
|
||||
}
|
||||
|
||||
const loadPublicTypes = async () => {
|
||||
try {
|
||||
const res = await request.post('/houtai/htdphqsplx', {
|
||||
username: localStorage.getItem('username') || ''
|
||||
})
|
||||
if (res.code === 0) {
|
||||
// 数据结构:{ code:0, data:{ public_types:[], kaiqi_shenhe: bool } }
|
||||
publicTypes.value = res.data.public_types || []
|
||||
auditMode.value = res.data.kaiqi_shenhe || false
|
||||
} else if (res.code === 403) {
|
||||
publicTypes.value = []
|
||||
ElMessage.error('无访问权限,页面已锁定')
|
||||
} else {
|
||||
ElMessage.error(res.msg || '商品类型加载失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('商品类型加载失败')
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 店铺列表 ==========
|
||||
const shopList = ref([])
|
||||
const selectedShopId = ref(null)
|
||||
const shopPage = reactive({ page: 1, size: 10, total: 0 })
|
||||
const shopSearch = reactive({ key: '', status: null })
|
||||
|
||||
const selectShop = (id) => {
|
||||
selectedShopId.value = id
|
||||
}
|
||||
|
||||
const loadShopList = async (page = 1) => {
|
||||
shopPage.page = page
|
||||
try {
|
||||
const res = await request.post('/houtai/htdphqdpys', {
|
||||
username: localStorage.getItem('username') || '',
|
||||
page: shopPage.page,
|
||||
page_size: shopPage.size,
|
||||
keyword: shopSearch.key,
|
||||
zhuangtai: shopSearch.status
|
||||
})
|
||||
if (res.code === 0) {
|
||||
shopList.value = res.data.list || []
|
||||
shopPage.total = res.data.total || 0
|
||||
} else if (res.code === 403) {
|
||||
shopList.value = []
|
||||
ElMessage.error('无权限访问店铺列表')
|
||||
} else {
|
||||
ElMessage.error(res.msg || '店铺列表加载失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('店铺列表加载失败')
|
||||
}
|
||||
}
|
||||
|
||||
const searchShops = () => {
|
||||
shopPage.page = 1
|
||||
loadShopList(1)
|
||||
}
|
||||
|
||||
// ========== 店铺商品类型映射 ==========
|
||||
const mappingList = ref([])
|
||||
const mappingLoading = ref(false)
|
||||
const mappingPage = reactive({ page: 1, size: 10, total: 0 })
|
||||
const pendingTotal = ref(0)
|
||||
const mappingSearch = reactive({ keyword: '', shangjia: null, fengjin: null, shenhe: null })
|
||||
|
||||
const loadMappings = async (page = 1) => {
|
||||
mappingLoading.value = true
|
||||
try {
|
||||
const res = await request.post('/houtai/htdphqyssplx', {
|
||||
username: localStorage.getItem('username') || '',
|
||||
page: page,
|
||||
page_size: mappingPage.size,
|
||||
keyword: mappingSearch.keyword,
|
||||
shangjia_zhuangtai: mappingSearch.shangjia,
|
||||
fengjin_zhuangtai: mappingSearch.fengjin,
|
||||
shenhe_zhuangtai: mappingSearch.shenhe,
|
||||
public_type_id: selectedPublicType.value,
|
||||
shop_id: selectedShopId.value
|
||||
})
|
||||
if (res.code === 0) {
|
||||
mappingList.value = res.data.list || []
|
||||
mappingPage.total = res.data.total || 0
|
||||
pendingTotal.value = res.data.pending_total || 0
|
||||
} else if (res.code === 403) {
|
||||
mappingList.value = []
|
||||
ElMessage.error('无权限访问')
|
||||
} else {
|
||||
ElMessage.error(res.msg || '加载失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('加载店铺商品类型失败')
|
||||
} finally {
|
||||
mappingLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const searchMappings = () => {
|
||||
mappingPage.page = 1
|
||||
loadMappings(1)
|
||||
}
|
||||
|
||||
// 监听上层筛选条件变化,自动刷新第三区域
|
||||
watch([selectedPublicType, selectedShopId], () => {
|
||||
mappingPage.page = 1
|
||||
loadMappings(1)
|
||||
}, { deep: true })
|
||||
|
||||
// ========== 映射详情修改 ==========
|
||||
const detailDialog = ref(false)
|
||||
const editingMapping = ref(null)
|
||||
|
||||
const openDetailDialog = (row) => {
|
||||
editingMapping.value = { ...row }
|
||||
detailDialog.value = true
|
||||
}
|
||||
|
||||
const confirmSaveMapping = () => {
|
||||
ElMessageBox.confirm('确定要保存修改吗?', '确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(() => {
|
||||
saveMappingDetail()
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
const saveMappingDetail = async () => {
|
||||
try {
|
||||
const res = await request.post('/houtai/htxgdpsplx', {
|
||||
action: 'update_mapping_status',
|
||||
username: localStorage.getItem('username') || '',
|
||||
mapping_id: editingMapping.value.id,
|
||||
shangjia_zhuangtai: editingMapping.value.shangjia_zhuangtai,
|
||||
fengjin_zhuangtai: editingMapping.value.fengjin_zhuangtai,
|
||||
shenhe_zhuangtai: editingMapping.value.shenhe_zhuangtai
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('修改成功')
|
||||
detailDialog.value = false
|
||||
loadMappings(mappingPage.page)
|
||||
} else {
|
||||
ElMessage.error(res.msg || '修改失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('请求失败')
|
||||
}
|
||||
}
|
||||
|
||||
const openProductDetail = (row) => {
|
||||
// 可跳转或弹窗
|
||||
ElMessage.info(`查看商品详情:${row.id}`)
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
onMounted(async () => {
|
||||
await loadPublicTypes() // 同时获取审核模式状态
|
||||
loadShopList()
|
||||
loadMappings()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.shop-product-manage {
|
||||
padding: 20px;
|
||||
}
|
||||
.top-card, .filter-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.card-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.switch-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.switch-desc {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
.type-tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.shop-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.shop-item {
|
||||
width: 200px;
|
||||
padding: 12px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.shop-item.active {
|
||||
border-color: #409eff;
|
||||
background: rgba(64,158,255,0.1);
|
||||
}
|
||||
.shop-item:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
.sub {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin: 4px 0;
|
||||
}
|
||||
.pending-badge {
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
8
src/views/shop/Withdrawapply.vue
Normal file
8
src/views/shop/Withdrawapply.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
8
src/views/shop/components/ProductOperationData.vue
Normal file
8
src/views/shop/components/ProductOperationData.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
8
src/views/shop/components/ShopOperationData.vue
Normal file
8
src/views/shop/components/ShopOperationData.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
395
src/views/shop/components/ShopProductList.vue
Normal file
395
src/views/shop/components/ShopProductList.vue
Normal file
@@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<div class="product-list-section">
|
||||
<!-- 搜索 + 筛选栏 -->
|
||||
<el-card shadow="never" class="filter-bar-card">
|
||||
<div class="filter-row">
|
||||
<el-input
|
||||
v-model="search.keyword"
|
||||
placeholder="商品ID / 标题"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
<el-select v-model="search.shenhe" placeholder="平台审核" clearable style="width: 120px">
|
||||
<el-option label="已通过" :value="true" />
|
||||
<el-option label="待审核" :value="false" />
|
||||
</el-select>
|
||||
<el-select v-model="search.shangjia" placeholder="上架状态" clearable style="width: 120px">
|
||||
<el-option label="已上架" :value="true" />
|
||||
<el-option label="已下架" :value="false" />
|
||||
</el-select>
|
||||
<el-select v-model="search.fengjin" placeholder="封禁状态" clearable style="width: 120px">
|
||||
<el-option label="未封禁" :value="false" />
|
||||
<el-option label="已封禁" :value="true" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 待审核数量提示 -->
|
||||
<div class="pending-tip" v-if="pendingCount > 0">
|
||||
<el-tag type="danger">待审核商品:{{ pendingCount }} 个</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 商品表格 -->
|
||||
<el-card shadow="never">
|
||||
<el-table :data="productList" v-loading="loading" stripe border style="width: 100%">
|
||||
<el-table-column prop="id" label="商品ID" width="70" />
|
||||
<el-table-column prop="biaoqian" label="商品标题" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="jiage" label="价格" width="90" />
|
||||
<el-table-column prop="kucun" label="库存" width="70" />
|
||||
<el-table-column label="平台审核" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.shenhe_zhuangtai ? 'success' : 'danger'" size="small">
|
||||
{{ row.shenhe_zhuangtai ? '通过' : '待审' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上架" width="70">
|
||||
<template #default="{ row }">
|
||||
<span :style="{ color: row.shangjia_zhuangtai ? '#67c23a' : '#f56c6c' }">
|
||||
{{ row.shangjia_zhuangtai ? '是' : '否' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="封禁" width="70">
|
||||
<template #default="{ row }">
|
||||
<span :style="{ color: row.fengjin_zhuangtai ? '#f56c6c' : '#67c23a' }">
|
||||
{{ row.fengjin_zhuangtai ? '是' : '否' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" @click="openDetail(row)">详情</el-button>
|
||||
<el-button size="small" type="success" @click="openOperationData(row.id)">运营数据</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="page.page"
|
||||
:page-size="page.size"
|
||||
:total="page.total"
|
||||
layout="total, prev, pager, next, jumper"
|
||||
@current-change="fetchList"
|
||||
style="margin-top: 16px; justify-content: flex-end;"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 详情 / 编辑弹窗 -->
|
||||
<el-dialog
|
||||
v-model="detailVisible"
|
||||
:title="isEditing ? '编辑商品' : '商品详情'"
|
||||
width="750px"
|
||||
destroy-on-close
|
||||
top="5vh"
|
||||
>
|
||||
<template v-if="currentProduct">
|
||||
<el-form :model="editForm" label-width="140px" :disabled="!isEditing" class="detail-form">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="商品ID">{{ currentProduct.id }}</el-form-item>
|
||||
<el-form-item label="商品标题">
|
||||
<el-input v-model="editForm.biaoqian" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商品价格">
|
||||
<el-input-number v-model="editForm.jiage" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="库存">
|
||||
<el-input-number v-model="editForm.kucun" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属公共类型">
|
||||
<template v-if="isEditing">
|
||||
<el-select v-model="editForm.leixing_id" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="pt in publicTypes"
|
||||
:key="pt.id"
|
||||
:label="pt.jieshao"
|
||||
:value="pt.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ getPublicTypeName(currentProduct.leixing_id) }}
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺商品类型">
|
||||
{{ getShopProductTypeName(currentProduct.dianpu_leixing_name) }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="真实销量">{{ currentProduct.zhenshi_xiaoliang }}</el-form-item>
|
||||
<el-form-item label="对外销量">
|
||||
<el-input-number v-model="editForm.duiwai_xiaoliang" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="抢单要求类型">
|
||||
<el-select v-model="editForm.yaoqiuleixing" :disabled="!isEditing" style="width: 100%">
|
||||
<el-option label="会员" :value="1" />
|
||||
<el-option label="佣金" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="editForm.yaoqiuleixing === 2" label="佣金">
|
||||
<el-input-number v-model="editForm.yongjin" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="会员ID">
|
||||
<el-input :model-value="currentProduct.huiyuan_id" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启额外分成">
|
||||
<el-switch v-model="editForm.kaioi_ewai_dashou_fencheng" :disabled="!isEditing" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="editForm.kaioi_ewai_dashou_fencheng" label="分成金额">
|
||||
<el-input-number v-model="editForm.ewai_dashou_fencheng" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="商品介绍">
|
||||
<el-input v-model="editForm.jieshao" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="下单须知">
|
||||
<el-input v-model="editForm.xiadan_xuzhi" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="上架状态">
|
||||
<el-switch v-model="editForm.shangjia_zhuangtai" :disabled="!isEditing" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="封禁状态">
|
||||
<el-switch v-model="editForm.fengjin_zhuangtai" :disabled="!isEditing" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="平台审核">
|
||||
<el-switch
|
||||
v-model="editForm.shenhe_zhuangtai"
|
||||
:disabled="!isEditing"
|
||||
active-text="通过"
|
||||
inactive-text="待审"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<div class="detail-actions">
|
||||
<template v-if="!isEditing">
|
||||
<el-button type="warning" @click="enterEdit">编辑</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button @click="cancelEdit">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSave">保存修改</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 运营数据弹窗(嵌套子组件) -->
|
||||
<el-dialog v-model="operationVisible" title="商品运营数据" width="80%" top="5vh" destroy-on-close>
|
||||
<ProductOperationData v-if="operationProductId" :product-id="operationProductId" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted, defineProps, defineEmits } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '@/utils/request'
|
||||
import ProductOperationData from './ProductOperationData.vue'
|
||||
|
||||
const props = defineProps({
|
||||
shopId: { type: Number, default: null },
|
||||
shopProductTypeId: { type: Number, default: null },
|
||||
publicTypeId: { type: Number, default: null },
|
||||
// 父组件传入的公共商品类型列表(必须在 Products.vue 中传递)
|
||||
publicTypes: { type: Array, default: () => [] }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['detail'])
|
||||
|
||||
// ========== 筛选条件 ==========
|
||||
const search = reactive({
|
||||
keyword: '',
|
||||
shenhe: null,
|
||||
shangjia: null,
|
||||
fengjin: null
|
||||
})
|
||||
|
||||
const resetSearch = () => {
|
||||
search.keyword = ''
|
||||
search.shenhe = null
|
||||
search.shangjia = null
|
||||
search.fengjin = null
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
// ========== 分页与列表 ==========
|
||||
const loading = ref(false)
|
||||
const productList = ref([])
|
||||
const page = reactive({ page: 1, size: 15, total: 0 })
|
||||
const pendingCount = ref(0)
|
||||
|
||||
const fetchList = async (pageNum = 1) => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await request.post('/houtai/hqhtdpsplx', {
|
||||
username: localStorage.getItem('username') || '',
|
||||
page: pageNum,
|
||||
page_size: page.size,
|
||||
shop_id: props.shopId,
|
||||
shop_product_type_id: props.shopProductTypeId,
|
||||
public_type_id: props.publicTypeId,
|
||||
keyword: search.keyword,
|
||||
shenhe_zhuangtai: search.shenhe,
|
||||
shangjia_zhuangtai: search.shangjia,
|
||||
fengjin_zhuangtai: search.fengjin
|
||||
})
|
||||
if (res.code === 0) {
|
||||
productList.value = res.data.list || []
|
||||
page.total = res.data.total || 0
|
||||
pendingCount.value = res.data.pending_total || 0
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取商品列表失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('网络异常')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
page.page = 1
|
||||
fetchList(1)
|
||||
}
|
||||
|
||||
// 监听父组件传入的筛选条件变化,自动重新查询(保持其他筛选不变)
|
||||
watch(() => [props.shopId, props.shopProductTypeId, props.publicTypeId], () => {
|
||||
page.page = 1
|
||||
fetchList(1)
|
||||
}, { deep: true })
|
||||
|
||||
// ========== 详情与编辑 ==========
|
||||
const detailVisible = ref(false)
|
||||
const isEditing = ref(false)
|
||||
const currentProduct = ref(null)
|
||||
const editForm = ref({})
|
||||
|
||||
const getPublicTypeName = (id) => {
|
||||
if (!id) return '未分配'
|
||||
const item = props.publicTypes.find(t => t.id === id)
|
||||
return item ? item.jieshao : '未知类型'
|
||||
}
|
||||
|
||||
const getShopProductTypeName = (name) => {
|
||||
// 后端应返回 dianpu_leixing_name,若没有则显示ID或占位
|
||||
return name || '无'
|
||||
}
|
||||
|
||||
const openDetail = (row) => {
|
||||
currentProduct.value = row
|
||||
// 深拷贝一份供编辑使用
|
||||
editForm.value = { ...row }
|
||||
isEditing.value = false
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
const enterEdit = () => {
|
||||
isEditing.value = true
|
||||
}
|
||||
|
||||
const cancelEdit = () => {
|
||||
// 恢复到原始数据(重新从 currentProduct 拷贝)
|
||||
editForm.value = { ...currentProduct.value }
|
||||
isEditing.value = false
|
||||
}
|
||||
|
||||
const confirmSave = () => {
|
||||
ElMessageBox.confirm('确定要保存商品修改吗?', '确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
doSave()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
const doSave = async () => {
|
||||
try {
|
||||
// 只传递实际被修改的字段,减少数据传输量
|
||||
const payload = {
|
||||
action: 'update_product',
|
||||
username: localStorage.getItem('username') || '',
|
||||
product_id: currentProduct.value.id,
|
||||
biaoqian: editForm.value.biaoqian,
|
||||
jiage: editForm.value.jiage,
|
||||
kucun: editForm.value.kucun,
|
||||
leixing_id: editForm.value.leixing_id,
|
||||
duiwai_xiaoliang: editForm.value.duiwai_xiaoliang,
|
||||
jieshao: editForm.value.jieshao,
|
||||
xiadan_xuzhi: editForm.value.xiadan_xuzhi,
|
||||
yaoqiuleixing: editForm.value.yaoqiuleixing,
|
||||
yongjin: editForm.value.yaoqiuleixing === 2 ? editForm.value.yongjin : null,
|
||||
kaioi_ewai_dashou_fencheng: editForm.value.kaioi_ewai_dashou_fencheng,
|
||||
ewai_dashou_fencheng: editForm.value.kaioi_ewai_dashou_fencheng ? editForm.value.ewai_dashou_fencheng : null,
|
||||
shangjia_zhuangtai: editForm.value.shangjia_zhuangtai,
|
||||
fengjin_zhuangtai: editForm.value.fengjin_zhuangtai,
|
||||
shenhe_zhuangtai: editForm.value.shenhe_zhuangtai
|
||||
}
|
||||
|
||||
const res = await request.post('/houtai/htxgdpspsj', payload)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('商品修改成功')
|
||||
detailVisible.value = false
|
||||
fetchList(page.page) // 刷新当前页
|
||||
} else {
|
||||
ElMessage.error(res.msg || '修改失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('请求异常')
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 运营数据 ==========
|
||||
const operationVisible = ref(false)
|
||||
const operationProductId = ref(null)
|
||||
|
||||
const openOperationData = (productId) => {
|
||||
operationProductId.value = productId
|
||||
operationVisible.value = true
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
onMounted(() => {
|
||||
fetchList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.product-list-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.filter-bar-card {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.filter-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
.pending-tip {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.detail-form .el-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.detail-actions {
|
||||
text-align: right;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user