Compare commits
10 Commits
12f80d3310
...
86abe6ee5f
| Author | SHA1 | Date |
|---|---|---|
|
|
86abe6ee5f | 6 months ago |
|
|
847b479108 | 7 months ago |
|
|
a424e632eb | 9 months ago |
|
|
544b02a07f | 9 months ago |
|
|
e176497ce3 | 9 months ago |
|
|
bb0b249cd9 | 9 months ago |
|
|
9b2b5ee7e6 | 9 months ago |
|
|
6dca1de96f | 10 months ago |
|
|
a7fdac9ce0 | 11 months ago |
|
|
3c7863a90a | 11 months ago |
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div class="order-type-chart">
|
||||
<div class="chart-header">
|
||||
<h3>订单类型分布</h3>
|
||||
</div>
|
||||
<v-chart class="chart" :option="chartOption" autoresize />
|
||||
<!-- <div class="legend">
|
||||
<div class="legend-item" v-for="item in legendData" :key="item.name">
|
||||
<span class="legend-color" :style="{ backgroundColor: item.color }"></span>
|
||||
<span class="legend-text">{{ item.name }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { PieChart } from 'echarts/charts'
|
||||
import { TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components'
|
||||
import VChart from 'vue-echarts'
|
||||
import RPC from '@/utils/rpc'
|
||||
|
||||
use([CanvasRenderer, PieChart, TitleComponent, TooltipComponent, LegendComponent])
|
||||
|
||||
const legendData = ref([
|
||||
{ name: '充值', color: '#409eff' },
|
||||
{ name: '包月', color: '#67c23a' },
|
||||
{ name: '包季', color: '#e6a23c' },
|
||||
{ name: '包年', color: '#f56c6c' },
|
||||
])
|
||||
|
||||
RPC.post('/dashboard/order_type_distribution').then((res) => {
|
||||
console.log('order_type_distribution', res)
|
||||
chartData.value = res.items.map((item) => {
|
||||
return {
|
||||
name: item.type,
|
||||
value: item.value_cents / 100,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const chartData = ref([
|
||||
// { value: 35, name: '充值', itemStyle: { color: '#409eff' } },
|
||||
// { value: 25, name: '包月', itemStyle: { color: '#67c23a' } },
|
||||
// { value: 25, name: '包季', itemStyle: { color: '#e6a23c' } },
|
||||
// { value: 15, name: '包年', itemStyle: { color: '#f56c6c' } },
|
||||
])
|
||||
|
||||
const chartOption = ref({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c}% ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
bottom: '0%',
|
||||
left: 'center',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '订单类型',
|
||||
type: 'pie',
|
||||
radius: ['50%', '80%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: chartData,
|
||||
},
|
||||
],
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-type-chart {
|
||||
padding: 20px;
|
||||
|
||||
.chart-header {
|
||||
margin-bottom: 20px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
margin-top: 20px;
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.legend-color {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.legend-text {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div class="recent-orders">
|
||||
<div class="card-header">
|
||||
<h3>最近订单</h3>
|
||||
<el-button type="primary" size="small" plain @click="goAll">查看全部</el-button>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<el-table :data="orderData" style="width: 100%" size="small" height="270">
|
||||
<el-table-column align="center" prop="order_no" label="订单编号" show-overflow-tooltip />
|
||||
<el-table-column align="center" prop="user_name" label="用户" show-overflow-tooltip />
|
||||
<el-table-column align="center" prop="amount" label="金额" width="100">
|
||||
<template #default="scope">
|
||||
<span class="amount">{{ scope.row.amount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="order_type" label="类型" width="100">
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.status === 'created'" type="warning" size="small">待付款</el-tag>
|
||||
<el-tag v-if="row.status === 'pending'" type="warning" size="small">待支付</el-tag>
|
||||
<el-tag v-if="row.status === 'paid'" type="success" size="small">已支付</el-tag>
|
||||
<el-tag v-if="row.status === 'failed'" type="danger" size="small">支付失败</el-tag>
|
||||
<el-tag v-if="row.status === 'canceled'" type="info" size="small">已取消</el-tag>
|
||||
<el-tag v-if="row.status === 'refunded'" type="info" size="small">已退款</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import RPC from '@/utils/rpc'
|
||||
import dayjs from 'dayjs'
|
||||
const router = useRouter()
|
||||
const goAll = () => router.push('/OrderManage')
|
||||
|
||||
const orderData = ref([])
|
||||
|
||||
const loading = ref(false)
|
||||
const getList = () => {
|
||||
loading.value = true
|
||||
RPC.post('/membership/order/list', { page: 1, size: 10 })
|
||||
.then((res) => {
|
||||
orderData.value = res.list
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
getList()
|
||||
|
||||
const getTypeTagType = (type) => {
|
||||
const typeMap = {
|
||||
包年: '',
|
||||
包季: 'success',
|
||||
充值: 'info',
|
||||
包月: 'warning',
|
||||
}
|
||||
return typeMap[type] || ''
|
||||
}
|
||||
|
||||
const getStatusTagType = (status) => {
|
||||
const statusMap = {
|
||||
已支付: 'success',
|
||||
待支付: 'warning',
|
||||
已取消: 'danger',
|
||||
}
|
||||
return statusMap[status] || ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.recent-orders {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
height: 100%;
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
.amount {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
.el-table__header {
|
||||
th {
|
||||
background-color: #fafafa;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__row {
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div class="revenue-chart">
|
||||
<div class="chart-header">
|
||||
<h3>收入趋势</h3>
|
||||
<div class="chart-tabs">
|
||||
<el-button
|
||||
size="small"
|
||||
:class="{ active: activeTab === 'month' }"
|
||||
@click="switchTab('month')"
|
||||
>
|
||||
月度
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
:class="{ active: activeTab === 'quarter' }"
|
||||
@click="switchTab('quarter')"
|
||||
>
|
||||
季度
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
:class="{ active: activeTab === 'year' }"
|
||||
@click="switchTab('year')"
|
||||
>
|
||||
年度
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<v-chart class="chart" :option="chartOption" autoresize />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { LineChart } from 'echarts/charts'
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent,
|
||||
} from 'echarts/components'
|
||||
import VChart from 'vue-echarts'
|
||||
import RPC from '@/utils/rpc'
|
||||
|
||||
use([CanvasRenderer, LineChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent])
|
||||
|
||||
const activeTab = ref('month')
|
||||
|
||||
const switchTab = (tab) => {
|
||||
activeTab.value = tab
|
||||
console.log(activeTab.value)
|
||||
getData()
|
||||
}
|
||||
|
||||
const data = ref([])
|
||||
const getData = () => {
|
||||
RPC.post('/dashboard/revenue_trend', { granularity: activeTab.value }).then((res) => {
|
||||
console.log(activeTab.value, res)
|
||||
data.value = res.items || []
|
||||
})
|
||||
}
|
||||
getData()
|
||||
|
||||
const chartOption = computed(() => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: '{b}: ¥{c}',
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: data.value.map((item) => item.label),
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#e4e7ed',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#606266',
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#606266',
|
||||
formatter: '{value}',
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f5f7fa',
|
||||
},
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
data: data.value.map((item) => item.value_cents / 100),
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#409eff',
|
||||
width: 3,
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#409eff',
|
||||
},
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(64, 158, 255, 0.3)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(64, 158, 255, 0.1)',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.revenue-chart {
|
||||
padding: 20px;
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.chart-tabs {
|
||||
.el-button {
|
||||
margin-left: 8px;
|
||||
|
||||
&.active {
|
||||
background-color: #157ee8;
|
||||
border-color: #177de3;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<div class="dashboard">
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-cards">
|
||||
<div class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-info">
|
||||
<div class="stat-label">总用户数</div>
|
||||
<div class="stat-value">{{ data.total_users }}</div>
|
||||
<div
|
||||
class="stat-trend"
|
||||
:class="{
|
||||
positive: data.total_users_mom_ratio?.includes('+'),
|
||||
negative:
|
||||
data.total_users_mom_ratio?.includes('-') &&
|
||||
data.total_users_mom_ratio.length > 1,
|
||||
}"
|
||||
>
|
||||
<el-icon v-if="data.total_users_mom_ratio?.includes('+')"><ArrowUp /></el-icon>
|
||||
<el-icon
|
||||
v-if="
|
||||
data.total_users_mom_ratio?.includes('-') && data.total_users_mom_ratio.length > 1
|
||||
"
|
||||
><ArrowDown
|
||||
/></el-icon>
|
||||
{{ data.total_users_mom_ratio }} 较上月
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-icon user">
|
||||
<el-icon><User /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-info">
|
||||
<div class="stat-label">今日活跃</div>
|
||||
<div class="stat-value">{{ data.active_today }}</div>
|
||||
<div
|
||||
class="stat-trend"
|
||||
:class="{
|
||||
positive: data.active_today_dod_ratio?.includes('+'),
|
||||
negative:
|
||||
data.active_today_dod_ratio?.includes('-') &&
|
||||
data.active_today_dod_ratio.length > 1,
|
||||
}"
|
||||
>
|
||||
<el-icon v-if="data.active_today_dod_ratio?.includes('+')"><ArrowUp /></el-icon>
|
||||
<el-icon
|
||||
v-if="
|
||||
data.active_today_dod_ratio?.includes('-') &&
|
||||
data.active_today_dod_ratio.length > 1
|
||||
"
|
||||
><ArrowDown
|
||||
/></el-icon>
|
||||
{{ data.active_today_dod_ratio }} 较昨日
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-icon active">
|
||||
<el-icon><TrendCharts /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-info">
|
||||
<div class="stat-label">商品生成量</div>
|
||||
<div class="stat-value">{{ data.drawings_generated }}</div>
|
||||
<div
|
||||
class="stat-trend"
|
||||
:class="{
|
||||
positive: data.drawings_generated_mom_ratio?.includes('+'),
|
||||
negative:
|
||||
data.drawings_generated_mom_ratio?.includes('-') &&
|
||||
data.drawings_generated_mom_ratio?.length > 1,
|
||||
}"
|
||||
>
|
||||
<el-icon v-if="data.drawings_generated_mom_ratio?.includes('+')"><ArrowUp /></el-icon>
|
||||
<el-icon
|
||||
v-if="
|
||||
data.drawings_generated_mom_ratio?.includes('-') &&
|
||||
data.drawings_generated_mom_ratio?.length > 1
|
||||
"
|
||||
><ArrowDown
|
||||
/></el-icon>
|
||||
{{ data.drawings_generated_mom_ratio }} 较上月
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-icon generate">
|
||||
<el-icon><Document /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-info">
|
||||
<div class="stat-label">本月收入</div>
|
||||
<div class="stat-value">¥{{ data.month_income_cents / 100 }}</div>
|
||||
<div
|
||||
class="stat-trend"
|
||||
:class="{
|
||||
positive: data.month_income_cents_mom_ratio?.includes('+'),
|
||||
negative:
|
||||
data.month_income_cents_mom_ratio?.includes('-') &&
|
||||
data.month_income_cents_mom_ratio?.length > 1,
|
||||
}"
|
||||
>
|
||||
<el-icon v-if="data.month_income_cents_mom_ratio?.includes('+')"><ArrowUp /></el-icon>
|
||||
<el-icon
|
||||
v-if="
|
||||
data.month_income_cents_mom_ratio?.includes('-') &&
|
||||
data.month_income_cents_mom_ratio?.length > 1
|
||||
"
|
||||
><ArrowDown
|
||||
/></el-icon>
|
||||
{{ data.month_income_cents_mom_ratio }} 较上月
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-icon revenue">
|
||||
<el-icon><Money /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图表区域 -->
|
||||
<div class="charts-section">
|
||||
<div class="chart-container">
|
||||
<RevenueChart />
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<OrderTypeChart />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近数据区域 -->
|
||||
<div class="recent-section">
|
||||
<div class="recent-container">
|
||||
<RecentOrders />
|
||||
</div>
|
||||
<div class="recent-container">
|
||||
<RecentDrawings />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ArrowUp, ArrowDown, User, TrendCharts, Document, Money } from '@element-plus/icons-vue'
|
||||
import RevenueChart from './RevenueChart.vue'
|
||||
import OrderTypeChart from './OrderTypeChart.vue'
|
||||
import RecentOrders from './RecentOrders.vue'
|
||||
import RecentDrawings from './RecentDrawings.vue'
|
||||
import RPC from '@/utils/rpc'
|
||||
|
||||
const data = ref({})
|
||||
const getData = () => {
|
||||
RPC.post('/dashboard/summary').then((res) => {
|
||||
data.value = res
|
||||
})
|
||||
}
|
||||
getData()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard {
|
||||
padding: 20px;
|
||||
// background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
|
||||
.stats-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.stat-info {
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-trend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
|
||||
&.positive {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.el-icon {
|
||||
font-size: 24px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.user {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
}
|
||||
|
||||
&.generate {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
}
|
||||
|
||||
&.revenue {
|
||||
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.charts-section {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
min-height: 400px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.recent-section {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.recent-container {
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,134 @@
|
||||
<script setup>
|
||||
import RPC from '@/utils/rpc'
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
row: [Object],
|
||||
templateList: [Array],
|
||||
})
|
||||
|
||||
const emit = defineEmits(['closed', 'onSuccess'])
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
const isCancel = computed(() => props.type === 'cancel')
|
||||
const isApprove = computed(() => props.type === 'approve')
|
||||
const isReject = computed(() => props.type === 'reject')
|
||||
const isApproval = computed(() => isApprove.value || isReject.value)
|
||||
|
||||
onMounted(() => {
|
||||
dialogVisible.value = true
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
const formField = ref({ remark: '' })
|
||||
const formRules = reactive({
|
||||
remark: [{ required: true, message: '备注不可为空', trigger: 'change' }],
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
function closed() {
|
||||
emit('closed')
|
||||
}
|
||||
|
||||
const submitForm = async () => {
|
||||
await formRef.value.validate()
|
||||
|
||||
if (isApproval.value) {
|
||||
// 审批逻辑
|
||||
const params = {
|
||||
application_id: props.row.id,
|
||||
action: isApprove.value ? 'approve' : 'reject',
|
||||
remark: formField.value.remark,
|
||||
}
|
||||
loading.value = true
|
||||
RPC.post('/template_access_review', params)
|
||||
.then((res) => {
|
||||
ElMessage.success('操作成功')
|
||||
emit('onSuccess')
|
||||
emit('closed')
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
const params = {
|
||||
template_id: props.row.template_id,
|
||||
user_id: props.row.user_id,
|
||||
remark: formField.value.remark,
|
||||
}
|
||||
loading.value = true
|
||||
RPC.post('/template_access_revoke', params)
|
||||
.then((res) => {
|
||||
ElMessage.success('操作成功')
|
||||
emit('onSuccess')
|
||||
emit('closed')
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
align-center
|
||||
:title="isApprove ? '同意申请' : isReject ? '拒绝申请' : isCancel ? '撤回权限' : ''"
|
||||
:width="isApproval ? '500px' : '80%'"
|
||||
v-model="dialogVisible"
|
||||
@closed="closed"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
label-position="right"
|
||||
label-suffix=":"
|
||||
:model="formField"
|
||||
:rules="formRules"
|
||||
v-loading="loading"
|
||||
label-width="150"
|
||||
>
|
||||
<!-- 审批表单 -->
|
||||
<template v-if="isApproval">
|
||||
<el-form-item label="申请用户">
|
||||
<span>{{ row?.nickname }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请原因">
|
||||
<span>{{ row?.reason }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="备注">
|
||||
<el-input type="textarea" :rows="4" placeholder="请输入备注" v-model="formField.remark" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else="isApproval">
|
||||
<el-form-item label="撤回用户">
|
||||
<span>{{ row?.nickname || row?.user_id }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="撤回模板">
|
||||
<span>
|
||||
{{
|
||||
templateList?.find((item) => item.id === row.template_id)?.name || row.template_id
|
||||
}}</span
|
||||
>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="备注">
|
||||
<el-input type="textarea" :rows="4" placeholder="请输入备注" v-model="formField.remark" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closed"> 取 消 </el-button>
|
||||
<el-button :type="isReject ? 'danger' : 'primary'" :loading="loading" @click="submitForm">
|
||||
{{ isApprove ? '同意' : isReject ? '拒绝' : '确定' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="title">模板审批</div>
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
class="form"
|
||||
:inline="true"
|
||||
label-width="98px"
|
||||
:model="queryParams"
|
||||
>
|
||||
<el-form-item label="申请用户" prop="nickname">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入"
|
||||
style="width: 220px"
|
||||
v-model="queryParams.nickname"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label-width="100px">
|
||||
<el-button icon="Search" type="primary" @click="handleQuery"> 搜索 </el-button>
|
||||
<el-button icon="RefreshRight" @click="resetQuery"> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div style="margin-bottom: 20px">
|
||||
<!-- <el-button icon="Plus" type="primary" @click="add"> 新建 </el-button> -->
|
||||
</div>
|
||||
<div ref="tableContainerEl" class="table_border table">
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
header-row-class-name="table-header"
|
||||
>
|
||||
<el-table-column align="center" label="申请用户" prop="nickname" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row.nickname || row.user_id }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="申请原因" prop="reason" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="申请模板" prop="template_id" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ templateList.find((item) => item.id === row.template_id)?.name || row.template_id }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="状态" prop="status">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.status === 'pending'">申请中</span>
|
||||
<span v-if="row.status === 'rejected'">已拒绝</span>
|
||||
<span v-if="row.status === 'approved'">已同意</span>
|
||||
<span v-if="row.status === 'revoked'">已撤回</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="备注" prop="remark" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="操作" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.status === 'pending'"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleApproval(row, 'approve')"
|
||||
>
|
||||
同意
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'approved'"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleApproval(row, 'cancel')"
|
||||
>
|
||||
撤回权限
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
v-if="row.status === 'pending'"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleApproval(row, 'reject')"
|
||||
>
|
||||
拒绝
|
||||
</el-button>
|
||||
<span v-if="row.status !== 'pending' && row.status !== 'approved'">--</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:limit="queryParams.limit"
|
||||
v-model:page="queryParams.page"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<HandleDialog
|
||||
v-if="isShowDialog"
|
||||
:type="rowType"
|
||||
:row="currentRow"
|
||||
@closed="isShowDialog = false"
|
||||
@on-success="getList"
|
||||
:templateList="templateList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useTableRPC } from '@/utils/hook'
|
||||
import HandleDialog from './HandleDialog.vue'
|
||||
import RPC from '@/utils/rpc.js'
|
||||
|
||||
const rowType = ref('')
|
||||
const currentRow = ref(null)
|
||||
const add = () => {
|
||||
isShowDialog.value = true
|
||||
currentRow.value = null
|
||||
rowType.value = 'add'
|
||||
}
|
||||
const edit = (row) => {
|
||||
isShowDialog.value = true
|
||||
rowType.value = 'edit'
|
||||
currentRow.value = row
|
||||
}
|
||||
|
||||
const templateList = ref([])
|
||||
RPC.post('/template/list', { page: 1, limit: -1 }).then((res) => {
|
||||
templateList.value = res.rows || []
|
||||
})
|
||||
|
||||
const {
|
||||
loading,
|
||||
tableData,
|
||||
tableRef,
|
||||
searchFormRef,
|
||||
total,
|
||||
queryParams,
|
||||
getList,
|
||||
handleQuery,
|
||||
resetQuery,
|
||||
handleDelete,
|
||||
isShowDialog,
|
||||
maxHeight,
|
||||
tableContainerEl,
|
||||
} = useTableRPC({
|
||||
queryParams: {
|
||||
orderby: [{ sort: 'asc' }],
|
||||
},
|
||||
remoteDataFn: '/template/access/list',
|
||||
// deleteConfig: {
|
||||
// remote: '/vendor/delete',
|
||||
// key: 'id',
|
||||
// message(row) {
|
||||
// return `确认删除 “${row.name}”`
|
||||
// },
|
||||
// },
|
||||
})
|
||||
|
||||
const handleApproval = (row, action) => {
|
||||
isShowDialog.value = true
|
||||
currentRow.value = row
|
||||
rowType.value = action
|
||||
}
|
||||
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,136 @@
|
||||
<script setup>
|
||||
import RPC from '@/utils/rpc'
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
row: [Object],
|
||||
})
|
||||
|
||||
const emit = defineEmits(['closed', 'onSuccess'])
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
const isAdd = computed(() => props.type === 'add')
|
||||
const isApprove = computed(() => props.type === 'approve')
|
||||
const isReject = computed(() => props.type === 'reject')
|
||||
const isApproval = computed(() => isApprove.value || isReject.value)
|
||||
|
||||
onMounted(() => {
|
||||
dialogVisible.value = true
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
const formField = ref({ enabled: true, params: [], remark: '' })
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '不可为空', trigger: 'change' }],
|
||||
code: [{ required: true, message: '不可为空', trigger: 'change' }],
|
||||
remark: [{ required: true, message: '备注不可为空', trigger: 'change' }],
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
function closed() {
|
||||
emit('closed')
|
||||
}
|
||||
|
||||
const submitForm = async () => {
|
||||
await formRef.value.validate()
|
||||
|
||||
if (isApproval.value) {
|
||||
// 审批逻辑
|
||||
const params = {
|
||||
application_id: props.row.id,
|
||||
action: isApprove.value ? 'approve' : 'reject',
|
||||
remark: formField.value.remark
|
||||
}
|
||||
loading.value = true
|
||||
RPC.post('/template_access_review', params)
|
||||
.then((res) => {
|
||||
ElMessage.success('操作成功')
|
||||
emit('onSuccess')
|
||||
emit('closed')
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
// 原有的增删改逻辑
|
||||
const api = isAdd.value ? '/vendor/create' : '/vendor/update'
|
||||
const params = {
|
||||
...formField.value,
|
||||
id: isAdd.value ? null : props.row.id,
|
||||
}
|
||||
loading.value = true
|
||||
RPC.post(api, params)
|
||||
.then((res) => {
|
||||
ElMessage.success('操作成功')
|
||||
emit('onSuccess')
|
||||
emit('closed')
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAdd.value && !isApproval.value) {
|
||||
formField.value = {
|
||||
...props.row,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
align-center
|
||||
:title="isApprove ? '同意申请' : isReject ? '拒绝申请' : isAdd ? '新增' : '修改'"
|
||||
:width="isApproval ? '500px' : '80%'"
|
||||
v-model="dialogVisible"
|
||||
@closed="closed"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
label-position="right"
|
||||
label-suffix=":"
|
||||
:model="formField"
|
||||
:rules="formRules"
|
||||
v-loading="loading"
|
||||
label-width="150"
|
||||
>
|
||||
<!-- 审批表单 -->
|
||||
<template v-if="isApproval">
|
||||
<el-form-item label="申请用户">
|
||||
<span>{{ row?.nickname }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请原因">
|
||||
<span>{{ row?.reason }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="备注">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入备注"
|
||||
v-model="formField.remark"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closed"> 取 消 </el-button>
|
||||
<el-button
|
||||
:type="isReject ? 'danger' : 'primary'"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ isApprove ? '同意' : isReject ? '拒绝' : '确定' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="title">需求管理</div>
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
class="form"
|
||||
:inline="true"
|
||||
label-width="98px"
|
||||
:model="queryParams"
|
||||
>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入"
|
||||
style="width: 220px"
|
||||
v-model="queryParams.name"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类" prop="category">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入"
|
||||
style="width: 220px"
|
||||
v-model="queryParams.category"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label-width="100px">
|
||||
<el-button icon="Search" type="primary" @click="handleQuery"> 搜索 </el-button>
|
||||
<el-button icon="RefreshRight" @click="resetQuery"> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div style="margin-bottom: 20px">
|
||||
<!-- <el-button icon="Plus" type="primary" @click="add"> 新建 </el-button> -->
|
||||
</div>
|
||||
<div ref="tableContainerEl" class="table_border table">
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
header-row-class-name="table-header"
|
||||
>
|
||||
<el-table-column align="center" label="分类" prop="category" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="名称" prop="name" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="描述" prop="description" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="提出用户" prop="proposer" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="邮箱" prop="email" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="手机" prop="phone" show-overflow-tooltip />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="联系厂商"
|
||||
prop="vendor_contact"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="模板名称"
|
||||
prop="template_name"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
|
||||
<el-table-column align="center" label="操作" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="danger" @click="handleDelete(row)"> 删除 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:limit="queryParams.limit"
|
||||
v-model:page="queryParams.page"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<HandleDialog
|
||||
v-if="isShowDialog"
|
||||
:type="rowType"
|
||||
:row="currentRow"
|
||||
@closed="isShowDialog = false"
|
||||
@on-success="getList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useTableRPC } from '@/utils/hook'
|
||||
import HandleDialog from './HandleDialog.vue'
|
||||
import RPC from '@/utils/rpc.js'
|
||||
|
||||
const {
|
||||
loading,
|
||||
tableData,
|
||||
tableRef,
|
||||
searchFormRef,
|
||||
total,
|
||||
queryParams,
|
||||
getList,
|
||||
handleQuery,
|
||||
resetQuery,
|
||||
handleDelete,
|
||||
isShowDialog,
|
||||
maxHeight,
|
||||
tableContainerEl,
|
||||
} = useTableRPC({
|
||||
queryParams: {
|
||||
orderby: [{ id: 'desc' }],
|
||||
},
|
||||
remoteDataFn: '/requirement/list',
|
||||
deleteConfig: {
|
||||
remote: '/requirement/delete',
|
||||
key: 'id',
|
||||
message(row) {
|
||||
return `确认删除 “${row.name}”`
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,103 @@
|
||||
<script setup>
|
||||
import RPC from '@/utils/rpc'
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
row: [Object],
|
||||
})
|
||||
|
||||
const emit = defineEmits(['closed', 'onSuccess'])
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
const isAdd = computed(() => props.type === 'add')
|
||||
|
||||
onMounted(() => {
|
||||
dialogVisible.value = true
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
const formField = ref({ enabled: true })
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '不可为空', trigger: 'change' }],
|
||||
code: [{ required: true, message: '不可为空', trigger: 'change' }],
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
function closed() {
|
||||
emit('closed')
|
||||
}
|
||||
|
||||
const submitForm = async () => {
|
||||
await formRef.value.validate()
|
||||
const api = isAdd.value ? '/vendor/create' : '/vendor/update'
|
||||
const params = {
|
||||
...formField.value,
|
||||
id: isAdd.value ? null : props.row.id,
|
||||
}
|
||||
loading.value = true
|
||||
RPC.post(api, params)
|
||||
.then((res) => {
|
||||
ElMessage.success('操作成功')
|
||||
emit('onSuccess')
|
||||
emit('closed')
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
if (!isAdd.value) {
|
||||
formField.value = {
|
||||
...props.row,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
align-center
|
||||
:title="isAdd ? '新增' : '修改'"
|
||||
width="80%"
|
||||
v-model="dialogVisible"
|
||||
@closed="closed"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
label-position="right"
|
||||
label-suffix=":"
|
||||
:model="formField"
|
||||
:rules="formRules"
|
||||
v-loading="loading"
|
||||
label-width="150"
|
||||
>
|
||||
<el-form-item prop="name" label="名称">
|
||||
<el-input placeholder="请输入" v-model="formField.name" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" label="字段">
|
||||
<el-input placeholder="请输入" v-model="formField.code" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="contact_name" label="联系人">
|
||||
<el-input placeholder="请输入" v-model="formField.contact_name" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="address" label="地址">
|
||||
<el-input placeholder="请输入" v-model="formField.address" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="enabled" label="是否启用">
|
||||
<el-switch v-model="formField.enabled" active-text="启用" inactive-text="禁用" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closed"> 取 消 </el-button>
|
||||
<el-button type="primary" :loading="loading" @click="submitForm"> 确 定 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="title">厂商管理</div>
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
class="form"
|
||||
:inline="true"
|
||||
label-width="98px"
|
||||
:model="queryParams"
|
||||
>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入"
|
||||
style="width: 220px"
|
||||
v-model="queryParams.name"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label-width="100px">
|
||||
<el-button icon="Search" type="primary" @click="handleQuery"> 搜索 </el-button>
|
||||
<el-button icon="RefreshRight" @click="resetQuery"> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div style="margin-bottom: 20px">
|
||||
<el-button icon="Plus" type="primary" @click="add"> 新建 </el-button>
|
||||
</div>
|
||||
<div ref="tableContainerEl" class="table_border table">
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
header-row-class-name="table-header"
|
||||
>
|
||||
<el-table-column align="center" label="名称" prop="name" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="字段" prop="code" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="联系人" prop="contact_name" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="地址" prop="address" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="是否启用" prop="enabled">
|
||||
<template #default="{ row }">
|
||||
{{ row.enabled ? '启用' : '禁用' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="edit(row)"> 编辑 </el-button>
|
||||
|
||||
<el-button link type="danger" @click="handleDelete(row)"> 删除 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:limit="queryParams.limit"
|
||||
v-model:page="queryParams.page"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<HandleDialog
|
||||
v-if="isShowDialog"
|
||||
:type="rowType"
|
||||
:row="currentRow"
|
||||
@closed="isShowDialog = false"
|
||||
@on-success="getList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useTableRPC } from '@/utils/hook'
|
||||
import HandleDialog from './HandleDialog.vue'
|
||||
|
||||
const rowType = ref('')
|
||||
const currentRow = ref(null)
|
||||
const add = () => {
|
||||
isShowDialog.value = true
|
||||
currentRow.value = null
|
||||
rowType.value = 'add'
|
||||
}
|
||||
const edit = (row) => {
|
||||
isShowDialog.value = true
|
||||
rowType.value = 'edit'
|
||||
currentRow.value = row
|
||||
}
|
||||
|
||||
const {
|
||||
loading,
|
||||
tableData,
|
||||
tableRef,
|
||||
searchFormRef,
|
||||
total,
|
||||
queryParams,
|
||||
getList,
|
||||
handleQuery,
|
||||
resetQuery,
|
||||
handleDelete,
|
||||
isShowDialog,
|
||||
maxHeight,
|
||||
tableContainerEl,
|
||||
} = useTableRPC({
|
||||
queryParams: {
|
||||
orderby: [{ id: 'desc' }],
|
||||
},
|
||||
remoteDataFn: '/vendor/list',
|
||||
deleteConfig: {
|
||||
remote: '/vendor/delete',
|
||||
key: 'id',
|
||||
message(row) {
|
||||
return `确认删除 “${row.name}”`
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in new issue