完善页面8
This commit is contained in:
@@ -21,359 +21,242 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="loading || !chartOption || !chartOption.series || chartOption.series.length === 0" class="chart-loading">
|
<view v-if="loading || !chartOption || chartOption == null" class="chart-loading">
|
||||||
<text>{{ loading ? '加载中...' : '暂无数据' }}</text>
|
<text>{{ loading ? '加载中...' : '暂无数据' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<EChartsView v-else class="chart-box" :option="chartOption" :key="'map-' + mapType" />
|
<EChartsView v-else class="chart-box" :option="chartOption" :key="'map-' + mapType" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="uts">
|
<script setup lang="uts">
|
||||||
|
import { ref, watch, onMounted } from 'vue'
|
||||||
import EChartsView from '@/uni_modules/charts/EChartsView.vue'
|
import EChartsView from '@/uni_modules/charts/EChartsView.vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售地域分布组件 (UTS / uni-app-x)
|
||||||
|
* 1:1 复刻数据分析中心地图分布
|
||||||
|
*/
|
||||||
|
|
||||||
type RegionDataItem = { name: string; value: number }
|
type RegionDataItem = { name: string; value: number }
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
startDate: { type: Date, required: true },
|
||||||
EChartsView
|
endDate: { type: Date, required: true },
|
||||||
},
|
topMerchants: { type: Array, default: () => [] as Array<any> },
|
||||||
props: {
|
loading: { type: Boolean, default: false }
|
||||||
startDate: { type: Date, required: true },
|
})
|
||||||
endDate: { type: Date, required: true },
|
|
||||||
topMerchants: { type: Array, default: () => [] },
|
const mapType = ref('china')
|
||||||
loading: { type: Boolean, default: false }
|
const regionData = ref<Array<RegionDataItem>>([])
|
||||||
},
|
const chartOption = ref<any>(null)
|
||||||
data() {
|
|
||||||
return {
|
// --- 数据处理逻辑 ---
|
||||||
mapType: 'china', // 'china' | 'world'
|
|
||||||
regionData: [] as Array<RegionDataItem>,
|
function toPlainObject(obj: any): any {
|
||||||
chartOption: {} as any
|
if (obj == null) return null
|
||||||
|
if (typeof obj !== 'object') return obj
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return (obj as Array<any>).map((item : any) : any => toPlainObject(item))
|
||||||
|
}
|
||||||
|
const plain: Record<string, any> = {}
|
||||||
|
const keys = Object.keys(obj as object)
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
const key = keys[i]
|
||||||
|
const value = (obj as Record<string, any>)[key]
|
||||||
|
if (typeof value === 'function' || key.startsWith('_') || key === 'toJSON') {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
},
|
if (value != null && typeof value === 'object' && !Array.isArray(value)) {
|
||||||
watch: {
|
plain[key] = toPlainObject(value)
|
||||||
startDate: { handler() { this.loadData() }, deep: true },
|
} else {
|
||||||
endDate: { handler() { this.loadData() }, deep: true },
|
plain[key] = value
|
||||||
topMerchants: { handler() { this.loadData() }, deep: true }
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.loadData()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
switchMapType(type: string) {
|
|
||||||
this.mapType = type
|
|
||||||
this.buildChartOption()
|
|
||||||
},
|
|
||||||
|
|
||||||
async loadData() {
|
|
||||||
try {
|
|
||||||
// 暂时使用模拟数据,后续可以创建 RPC 函数获取真实省份数据
|
|
||||||
// 基于商家数据生成省份分布(模拟)
|
|
||||||
const mockProvinces: Array<RegionDataItem> = [
|
|
||||||
{ name: '广东', value: 0 },
|
|
||||||
{ name: '北京', value: 0 },
|
|
||||||
{ name: '上海', value: 0 },
|
|
||||||
{ name: '浙江', value: 0 },
|
|
||||||
{ name: '江苏', value: 0 },
|
|
||||||
{ name: '山东', value: 0 },
|
|
||||||
{ name: '河南', value: 0 },
|
|
||||||
{ name: '四川', value: 0 },
|
|
||||||
{ name: '湖北', value: 0 },
|
|
||||||
{ name: '湖南', value: 0 },
|
|
||||||
{ name: '福建', value: 0 },
|
|
||||||
{ name: '安徽', value: 0 },
|
|
||||||
{ name: '河北', value: 0 },
|
|
||||||
{ name: '陕西', value: 0 },
|
|
||||||
{ name: '江西', value: 0 },
|
|
||||||
{ name: '重庆', value: 0 },
|
|
||||||
{ name: '辽宁', value: 0 },
|
|
||||||
{ name: '云南', value: 0 },
|
|
||||||
{ name: '广西', value: 0 },
|
|
||||||
{ name: '山西', value: 0 },
|
|
||||||
{ name: '内蒙古', value: 0 },
|
|
||||||
{ name: '贵州', value: 0 },
|
|
||||||
{ name: '新疆', value: 0 },
|
|
||||||
{ name: '天津', value: 0 },
|
|
||||||
{ name: '吉林', value: 0 },
|
|
||||||
{ name: '黑龙江', value: 0 },
|
|
||||||
{ name: '海南', value: 0 },
|
|
||||||
{ name: '甘肃', value: 0 },
|
|
||||||
{ name: '宁夏', value: 0 },
|
|
||||||
{ name: '青海', value: 0 },
|
|
||||||
{ name: '西藏', value: 0 }
|
|
||||||
]
|
|
||||||
|
|
||||||
// 如果有商家数据,可以基于商家数量或 GMV 分配省份
|
|
||||||
const merchants = this.topMerchants as Array<any>
|
|
||||||
const totalSales = merchants.reduce((sum: number, m: any) => {
|
|
||||||
return sum + (Number(m.sales) || 0)
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
if (totalSales > 0 && merchants.length > 0) {
|
|
||||||
// 基于商家数量分配省份
|
|
||||||
const merchantCount = merchants.length
|
|
||||||
for (let i = 0; i < Math.min(merchantCount, mockProvinces.length); i++) {
|
|
||||||
const sales = Number(merchants[i].sales) || 0
|
|
||||||
mockProvinces[i].value = Math.round(sales * (Math.random() * 0.5 + 0.5))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 使用随机数据
|
|
||||||
for (let i = 0; i < mockProvinces.length; i++) {
|
|
||||||
mockProvinces[i].value = Math.round(Math.random() * 100000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.regionData = mockProvinces
|
|
||||||
this.buildChartOption()
|
|
||||||
} catch (e) {
|
|
||||||
console.error('❌ AnalyticsRegionMap loadData failed', e)
|
|
||||||
this.regionData = []
|
|
||||||
this.buildChartOption()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
buildChartOption() {
|
|
||||||
if (!this.regionData || this.regionData.length === 0) {
|
|
||||||
this.chartOption = {}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxValue = Math.max(...this.regionData.map((d) => d.value), 1)
|
|
||||||
|
|
||||||
if (this.mapType === 'china') {
|
|
||||||
// 中国地图配置(使用 geo 组件,兼容性更好)
|
|
||||||
this.chartOption = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
formatter: (params: any) => {
|
|
||||||
const name = params.name || '未知'
|
|
||||||
const value = params.value || 0
|
|
||||||
return `${name}<br/>销售额: ¥${this.formatMoney(value)}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
visualMap: {
|
|
||||||
min: 0,
|
|
||||||
max: maxValue,
|
|
||||||
left: 'left',
|
|
||||||
top: 'bottom',
|
|
||||||
text: ['高', '低'],
|
|
||||||
inRange: {
|
|
||||||
color: ['#e0f2fe', '#0ea5e9', '#0284c7', '#0369a1']
|
|
||||||
},
|
|
||||||
calculable: true,
|
|
||||||
textStyle: {
|
|
||||||
color: 'rgba(0,0,0,0.65)',
|
|
||||||
fontSize: 12
|
|
||||||
}
|
|
||||||
},
|
|
||||||
geo: {
|
|
||||||
map: 'china',
|
|
||||||
roam: false,
|
|
||||||
zoom: 1.5,
|
|
||||||
top: '40%',
|
|
||||||
itemStyle: {
|
|
||||||
areaColor: '#f0f0f0',
|
|
||||||
borderColor: '#fff',
|
|
||||||
borderWidth: 1
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
areaColor: '#0ea5e9'
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
color: '#111'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '销售额',
|
|
||||||
type: 'map',
|
|
||||||
map: 'china',
|
|
||||||
geoIndex: 0,
|
|
||||||
data: this.regionData,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
fontSize: 11,
|
|
||||||
color: 'rgba(0,0,0,0.75)'
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
borderColor: '#fff',
|
|
||||||
borderWidth: 1
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
areaColor: '#0ea5e9',
|
|
||||||
borderColor: '#fff',
|
|
||||||
borderWidth: 2
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 全国地图配置(简化版,使用柱状图展示 TOP 省份)
|
|
||||||
this.chartOption = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: { type: 'shadow' }
|
|
||||||
},
|
|
||||||
grid: { left: 60, right: 20, top: 36, bottom: 60 },
|
|
||||||
xAxis: {
|
|
||||||
type: 'category',
|
|
||||||
data: this.regionData
|
|
||||||
.sort((a, b) => b.value - a.value)
|
|
||||||
.slice(0, 15)
|
|
||||||
.map((d) => d.name),
|
|
||||||
axisLabel: {
|
|
||||||
rotate: 45,
|
|
||||||
color: 'rgba(0,0,0,0.55)',
|
|
||||||
fontSize: 12
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'value',
|
|
||||||
axisLabel: {
|
|
||||||
color: 'rgba(0,0,0,0.55)',
|
|
||||||
formatter: (value: number) => {
|
|
||||||
if (value >= 10000) return (value / 10000).toFixed(1) + '万'
|
|
||||||
return String(Math.round(value))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
lineStyle: { color: 'rgba(0,0,0,0.06)' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '销售额',
|
|
||||||
type: 'bar',
|
|
||||||
data: this.regionData
|
|
||||||
.sort((a, b) => b.value - a.value)
|
|
||||||
.slice(0, 15)
|
|
||||||
.map((d) => d.value),
|
|
||||||
barWidth: 18,
|
|
||||||
itemStyle: {
|
|
||||||
borderRadius: [6, 6, 0, 0],
|
|
||||||
color: '#3b82f6'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换为纯 JS 对象
|
|
||||||
this.chartOption = this.toPlainObject(this.chartOption)
|
|
||||||
},
|
|
||||||
|
|
||||||
formatMoney(n: number): string {
|
|
||||||
const v = isFinite(n) ? n : 0
|
|
||||||
if (v >= 10000) return (v / 10000).toFixed(1) + '万'
|
|
||||||
return v.toFixed(0)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 工具函数:将 UTS 对象转换为纯 JavaScript 对象
|
|
||||||
toPlainObject(obj: any): any {
|
|
||||||
if (obj == null) return null
|
|
||||||
if (typeof obj !== 'object') return obj
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
return obj.map((item) => this.toPlainObject(item))
|
|
||||||
}
|
|
||||||
const plain: any = {}
|
|
||||||
for (const key in obj) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
||||||
const value = obj[key]
|
|
||||||
if (typeof value === 'function' || key.startsWith('_') || key === 'toJSON') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (value != null && typeof value === 'object' && !Array.isArray(value)) {
|
|
||||||
let isSimple = true
|
|
||||||
for (const k in value) {
|
|
||||||
if (typeof value[k] === 'object' && value[k] !== null) {
|
|
||||||
isSimple = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
plain[key] = isSimple ? { ...value } : this.toPlainObject(value)
|
|
||||||
} else {
|
|
||||||
plain[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return plain
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return plain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildChartOption() {
|
||||||
|
if (regionData.value.length === 0) {
|
||||||
|
chartOption.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentData = regionData.value
|
||||||
|
const maxValue = Math.max(...currentData.map((d: RegionDataItem): number => d.value), 1)
|
||||||
|
|
||||||
|
let option: any = {}
|
||||||
|
|
||||||
|
if (mapType.value === 'china') {
|
||||||
|
option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item'
|
||||||
|
},
|
||||||
|
visualMap: {
|
||||||
|
min: 0,
|
||||||
|
max: maxValue,
|
||||||
|
left: 'left',
|
||||||
|
top: 'bottom',
|
||||||
|
text: ['高', '低'],
|
||||||
|
inRange: {
|
||||||
|
color: ['#e0f2fe', '#0ea5e9', '#0284c7', '#0369a1']
|
||||||
|
},
|
||||||
|
calculable: true,
|
||||||
|
textStyle: {
|
||||||
|
color: 'rgba(0,0,0,0.65)',
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
geo: {
|
||||||
|
map: 'china',
|
||||||
|
roam: false,
|
||||||
|
zoom: 1.5,
|
||||||
|
top: '40%',
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: '#f0f0f0',
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 1
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: '#0ea5e9'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
color: '#111'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销售额',
|
||||||
|
type: 'map',
|
||||||
|
map: 'china',
|
||||||
|
geoIndex: 0,
|
||||||
|
data: currentData,
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 11,
|
||||||
|
color: 'rgba(0,0,0,0.75)'
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 1
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: '#0ea5e9',
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: { type: 'shadow' }
|
||||||
|
},
|
||||||
|
grid: { left: 60, right: 20, top: 36, bottom: 60 },
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: currentData
|
||||||
|
.sort((a, b) => b.value - a.value)
|
||||||
|
.slice(0, 15)
|
||||||
|
.map((d) => d.name),
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45,
|
||||||
|
color: 'rgba(0,0,0,0.55)',
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
color: 'rgba(0,0,0,0.55)'
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: { color: 'rgba(0,0,0,0.06)' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销售额',
|
||||||
|
type: 'bar',
|
||||||
|
data: currentData
|
||||||
|
.sort((a, b) => b.value - a.value)
|
||||||
|
.slice(0, 15)
|
||||||
|
.map((d) => d.value),
|
||||||
|
barWidth: 18,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: [6, 6, 0, 0],
|
||||||
|
color: '#3b82f6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chartOption.value = toPlainObject(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadData() {
|
||||||
|
const mockProvinces: Array<RegionDataItem> = [
|
||||||
|
{ name: '广东', value: 0 }, { name: '北京', value: 0 }, { name: '上海', value: 0 },
|
||||||
|
{ name: '浙江', value: 0 }, { name: '江苏', value: 0 }, { name: '山东', value: 0 },
|
||||||
|
{ name: '河南', value: 0 }, { name: '四川', value: 0 }, { name: '湖北', value: 0 },
|
||||||
|
{ name: '湖南', value: 0 }, { name: '福建', value: 0 }, { name: '安徽', value: 0 },
|
||||||
|
{ name: '河北', value: 0 }, { name: '陕西', value: 0 }, { name: '江西', value: 0 },
|
||||||
|
{ name: '重庆', value: 0 }, { name: '辽宁', value: 0 }, { name: '云南', value: 0 },
|
||||||
|
{ name: '广西', value: 0 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const merchants = props.topMerchants as Array<any>
|
||||||
|
if (merchants.length > 0) {
|
||||||
|
for (let i = 0; i < Math.min(merchants.length, mockProvinces.length); i++) {
|
||||||
|
const salesStr = String(merchants[i].sales || '0')
|
||||||
|
const sales = parseFloat(salesStr)
|
||||||
|
mockProvinces[i].value = Math.round(sales * (Math.random() * 0.3 + 0.7))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < mockProvinces.length; i++) {
|
||||||
|
mockProvinces[i].value = Math.round(Math.random() * 100000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
regionData.value = mockProvinces
|
||||||
|
buildChartOption()
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchMapType(type: string) {
|
||||||
|
mapType.value = type
|
||||||
|
buildChartOption()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch([() => props.startDate, () => props.endDate, () => props.topMerchants], () => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
.region-map {
|
.region-map { width: 100%; }
|
||||||
width: 100%;
|
.map-head { margin-bottom: 8px; }
|
||||||
}
|
.map-head-left { display: flex; flex-direction: row; align-items: center; gap: 12px; flex: 1; }
|
||||||
|
.map-title { font-size: 14px; font-weight: 600; color: #111; }
|
||||||
.map-head {
|
.map-switch { display: flex; flex-direction: row; gap: 4px; background: #f3f4f6; border-radius: 8px; padding: 2px; }
|
||||||
margin-bottom: 8px;
|
.map-switch-btn { padding: 6px 12px; border-radius: 6px; font-size: 12px; color: rgba(0,0,0,0.65); background: transparent; cursor: pointer; transition: all 0.2s; white-space: nowrap; }
|
||||||
}
|
.map-switch-btn.active { background: #fff; color: #111; font-weight: 500; box-shadow: 0 1px 2px rgba(0,0,0,0.1); }
|
||||||
|
.chart-box { width: 100%; height: 360px; }
|
||||||
.map-head-left {
|
.chart-loading { width: 100%; height: 360px; display: flex; align-items: center; justify-content: center; color: rgba(0,0,0,0.45); font-size: 14px; }
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-title {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-switch {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 4px;
|
|
||||||
background: #f3f4f6;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-switch-btn {
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: rgba(0,0,0,0.65);
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-switch-btn.active {
|
|
||||||
background: #fff;
|
|
||||||
color: #111;
|
|
||||||
font-weight: 500;
|
|
||||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-box {
|
|
||||||
width: 100%;
|
|
||||||
height: 360px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading {
|
|
||||||
width: 100%;
|
|
||||||
height: 360px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: rgba(0,0,0,0.45);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
import AdminAside from '@/layouts/admin/components/AdminAside.uvue'
|
import AdminAside from '@/layouts/admin/components/AdminAside.uvue'
|
||||||
import AdminSubSider from '@/layouts/admin/components/AdminSubSider.uvue'
|
import AdminSubSider from '@/layouts/admin/components/AdminSubSider.uvue'
|
||||||
import AdminHeader from '@/layouts/admin/components/AdminHeader.uvue'
|
import AdminHeader from '@/layouts/admin/components/AdminHeader.uvue'
|
||||||
@@ -418,16 +418,16 @@ onMounted(() => {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.layout-root {
|
.layout-root {
|
||||||
--admin-page-padding-desktop: 12px;
|
--admin-page-padding-desktop: 20px;
|
||||||
--admin-page-padding-mobile: 8px;
|
--admin-page-padding-mobile: 10px;
|
||||||
--admin-section-gap: 12px;
|
--admin-section-gap: 20px;
|
||||||
--admin-card-padding: 16px;
|
--admin-card-padding: 24px;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f0f2f5;
|
background: #f5f7f9;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,7 +471,7 @@ onMounted(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
transition: margin-left 300ms ease;
|
transition: margin-left 300ms ease;
|
||||||
background: #f0f2f5;
|
background: #f5f7f9;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ onMounted(() => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
overflow-x: auto; /* 允许横向滚动,兼容极端窄屏 */
|
overflow-x: auto; /* 允许横向滚动,兼容极端窄屏 */
|
||||||
background: #f0f2f5;
|
background: #f5f7f9;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,11 +53,15 @@ function getIconText(icon: string): string {
|
|||||||
'product': '📦',
|
'product': '📦',
|
||||||
'order': '📜',
|
'order': '📜',
|
||||||
'marketing': '📉',
|
'marketing': '📉',
|
||||||
'content': '📝',
|
'share': '✈️',
|
||||||
|
'customer-service': '💬',
|
||||||
'finance': '💰',
|
'finance': '💰',
|
||||||
'statistic': '📊',
|
'content': '📝',
|
||||||
|
'decoration': '🎨',
|
||||||
|
'app': '📱',
|
||||||
'setting': '⚙️',
|
'setting': '⚙️',
|
||||||
'maintenance': '🛠️'
|
'tool': '🛠️',
|
||||||
|
'statistic': '📊'
|
||||||
}
|
}
|
||||||
return iconMap[icon] || icon.charAt(0).toUpperCase()
|
return iconMap[icon] || icon.charAt(0).toUpperCase()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,25 +6,172 @@
|
|||||||
* value: 组件引用
|
* value: 组件引用
|
||||||
*
|
*
|
||||||
* 注意:
|
* 注意:
|
||||||
* 1. 组件已切换为 defineAsyncComponent 异步导入,优化 H5 环境下的加载性能与包体积
|
* 1. 组件已切换为 静态导入 (Static Import),以解决 H5 环境下的加载异常 (net::ERR_CACHE_READ_FAILURE)
|
||||||
* 2. 组件路径使用 @ 别名
|
* 2. 组件路径使用 @ 别名
|
||||||
* 3. 占位组件统一使用 PlaceholderPage
|
* 3. 占位组件统一使用 PlaceholderPage
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
|
|
||||||
// 导入占位组件
|
// 导入占位组件
|
||||||
import PlaceholderPage from '@/layouts/admin/components/PlaceholderPage.uvue'
|
import PlaceholderPage from '@/layouts/admin/components/PlaceholderPage.uvue'
|
||||||
|
|
||||||
// 导入首页(内部组件,不包含 AdminLayout)
|
// 导入首页(内部组件,不包含 AdminLayout)
|
||||||
import HomeIndex from '@/layouts/admin/pages/HomeIndex.uvue'
|
import HomeIndex from '@/layouts/admin/pages/HomeIndex.uvue'
|
||||||
|
|
||||||
// 用户、商品、订单模块已改为 defineAsyncComponent 异步加载,移除静态导入以优化 H5 加载性能
|
// --- 用户模块 ---
|
||||||
|
import UserStatistic from '@/pages/mall/admin/user/statistics/index.uvue'
|
||||||
|
import UserList from '@/pages/mall/admin/user/management/index.uvue'
|
||||||
|
import UserLevel from '@/pages/mall/admin/user/level/index.uvue'
|
||||||
|
import UserGroup from '@/pages/mall/admin/user/grouping/index.uvue'
|
||||||
|
import UserLabel from '@/pages/mall/admin/user/label/index.uvue'
|
||||||
|
import UserMemberConfig from '@/pages/mall/admin/user/configuration/index.uvue'
|
||||||
|
|
||||||
// 营销设置模块暂时使用 PlaceholderPage
|
// --- 商品模块 ---
|
||||||
// 避免循环依赖问题
|
import ProductStatistic from '@/pages/mall/admin/product/product-statistics/index.uvue'
|
||||||
|
import ProductList from '@/pages/mall/admin/product/product-management/index.uvue'
|
||||||
|
import ProductEdit from '@/pages/mall/admin/product/product-management/edit.uvue'
|
||||||
|
import ProductMemberPrice from '@/pages/mall/admin/product/product-management/member-price.uvue'
|
||||||
|
import ProductClassify from '@/pages/mall/admin/product/classification/index.uvue'
|
||||||
|
import ProductReply from '@/pages/mall/admin/product/reviews/index.uvue'
|
||||||
|
import ProductAttr from '@/pages/mall/admin/product/specifications/index.uvue'
|
||||||
|
import ProductParam from '@/pages/mall/admin/product/parameters/index.uvue'
|
||||||
|
import ProductLabel from '@/pages/mall/admin/product/labels/index.uvue'
|
||||||
|
import ProductProtection from '@/pages/mall/admin/product/protection/index.uvue'
|
||||||
|
|
||||||
// 营销、内容、财务、客服、装修等模块已改为 defineAsyncComponent 异步加载,移除静态导入以优化 H5 加载性能
|
// --- 订单模块 ---
|
||||||
|
import OrderList from '@/pages/mall/admin/order/list.uvue'
|
||||||
|
import OrderStatistic from '@/pages/mall/admin/order/order-statistics/index.uvue'
|
||||||
|
import OrderRefund from '@/pages/mall/admin/order/aftersales-order/index.uvue'
|
||||||
|
import OrderCashier from '@/pages/mall/admin/order/cashier-order/index.uvue'
|
||||||
|
import OrderVerify from '@/pages/mall/admin/order/write-off-records/index.uvue'
|
||||||
|
import OrderConfig from '@/pages/mall/admin/order/order-configuration/index.uvue'
|
||||||
|
|
||||||
|
// --- 营销模块 ---
|
||||||
|
import MarketingCouponList from '@/pages/mall/admin/marketing/coupon/list.uvue'
|
||||||
|
import MarketingCouponUser from '@/pages/mall/admin/marketing/coupon/user.uvue'
|
||||||
|
import MarketingIntegralStatistic from '@/pages/mall/admin/marketing/integral/statistic.uvue'
|
||||||
|
import MarketingIntegralProduct from '@/pages/mall/admin/marketing/integral/list.uvue'
|
||||||
|
import MarketingIntegralOrder from '@/pages/mall/admin/marketing/integral/order.uvue'
|
||||||
|
import MarketingIntegralRecord from '@/pages/mall/admin/marketing/integral/record.uvue'
|
||||||
|
import MarketingIntegralConfig from '@/pages/mall/admin/marketing/integral/config.uvue'
|
||||||
|
import MarketingLotteryList from '@/pages/mall/admin/marketing/lottery/list.uvue'
|
||||||
|
import MarketingLotteryConfig from '@/pages/mall/admin/marketing/lottery/config.uvue'
|
||||||
|
import MarketingCombinationProduct from '@/pages/mall/admin/marketing/combination/product.uvue'
|
||||||
|
import MarketingCombinationList from '@/pages/mall/admin/marketing/combination/list.uvue'
|
||||||
|
import MarketingCombinationCreate from '@/pages/mall/admin/marketing/combination/create.uvue'
|
||||||
|
import MarketingSeckillList from '@/pages/mall/admin/marketing/seckill/list.uvue'
|
||||||
|
import MarketingSeckillProduct from '@/pages/mall/admin/marketing/seckill/product.uvue'
|
||||||
|
import MarketingSeckillConfig from '@/pages/mall/admin/marketing/seckill/config.uvue'
|
||||||
|
import MarketingMemberType from '@/pages/mall/admin/marketing/member/type.uvue'
|
||||||
|
import MarketingMemberRight from '@/pages/mall/admin/marketing/member/right.uvue'
|
||||||
|
import MarketingMemberCard from '@/pages/mall/admin/marketing/member/card.uvue'
|
||||||
|
import MarketingMemberRecord from '@/pages/mall/admin/marketing/member/record.uvue'
|
||||||
|
import MarketingMemberConfig from '@/pages/mall/admin/marketing/member/config.uvue'
|
||||||
|
import MarketingLiveRoom from '@/pages/mall/admin/marketing/live/room.uvue'
|
||||||
|
import MarketingLiveProduct from '@/pages/mall/admin/marketing/live/product.uvue'
|
||||||
|
import MarketingLiveAnchor from '@/pages/mall/admin/marketing/live/anchor.uvue'
|
||||||
|
import MarketingRechargeQuota from '@/pages/mall/admin/marketing/recharge/quota.uvue'
|
||||||
|
import MarketingRechargeConfig from '@/pages/mall/admin/marketing/recharge/config.uvue'
|
||||||
|
import MarketingCheckinConfig from '@/pages/mall/admin/marketing/checkin/config.uvue'
|
||||||
|
import MarketingCheckinReward from '@/pages/mall/admin/marketing/checkin/reward.uvue'
|
||||||
|
import MarketingNewcomerGift from '@/pages/mall/admin/marketing/newcomer/index.uvue'
|
||||||
|
|
||||||
|
// --- 内容模块 ---
|
||||||
|
import CmsArticle from '@/pages/mall/admin/cms/article/list.uvue'
|
||||||
|
import CmsCategory from '@/pages/mall/admin/cms/category/list.uvue'
|
||||||
|
|
||||||
|
// --- 财务模块 ---
|
||||||
|
import FinanceTransactionStats from '@/pages/mall/admin/finance/transaction_stats.uvue'
|
||||||
|
import FinanceWithdrawal from '@/pages/mall/admin/finance/withdrawal.uvue'
|
||||||
|
import FinanceInvoice from '@/pages/mall/admin/finance/invoice.uvue'
|
||||||
|
import FinanceRecharge from '@/pages/mall/admin/finance/recharge.uvue'
|
||||||
|
import FinanceCapitalFlow from '@/pages/mall/admin/finance/capital_flow.uvue'
|
||||||
|
import FinanceBill from '@/pages/mall/admin/finance/bill.uvue'
|
||||||
|
import FinanceCommission from '@/pages/mall/admin/finance/commission.uvue'
|
||||||
|
import FinanceBalanceStats from '@/pages/mall/admin/finance/balance_stats.uvue'
|
||||||
|
import FinanceBalanceRecord from '@/pages/mall/admin/finance/balance_record.uvue'
|
||||||
|
|
||||||
|
// --- 设置模块 ---
|
||||||
|
import SettingSystemConfig from '@/pages/mall/admin/setting/system/config.uvue'
|
||||||
|
import SettingMessageIndex from '@/pages/mall/admin/setting/message.uvue'
|
||||||
|
import SettingProtocolIndex from '@/pages/mall/admin/setting/agreement.uvue'
|
||||||
|
import SettingTicketIndex from '@/pages/mall/admin/setting/ticket.uvue'
|
||||||
|
import SettingAuthRole from '@/pages/mall/admin/setting/auth/role.uvue'
|
||||||
|
import SettingAuthAdmin from '@/pages/mall/admin/setting/auth/admin.uvue'
|
||||||
|
import SettingAuthPermission from '@/pages/mall/admin/setting/auth/permission.uvue'
|
||||||
|
import SettingDeliveryStaff from '@/pages/mall/admin/setting/delivery/staff.uvue'
|
||||||
|
import SettingDeliveryStation from '@/pages/mall/admin/setting/delivery/station.uvue'
|
||||||
|
import SettingDeliveryVerifier from '@/pages/mall/admin/setting/delivery/verifier.uvue'
|
||||||
|
import SettingDeliveryTemplate from '@/pages/mall/admin/setting/delivery/template.uvue'
|
||||||
|
import SettingInterfaceOnepassConfig from '@/pages/mall/admin/setting/interface/onepass/config.uvue'
|
||||||
|
import SettingInterfaceOnepassIndex from '@/pages/mall/admin/setting/interface/onepass/index.uvue'
|
||||||
|
import SettingInterfaceStorage from '@/pages/mall/admin/setting/interface/storage.uvue'
|
||||||
|
import SettingInterfaceCollect from '@/pages/mall/admin/setting/interface/collect.uvue'
|
||||||
|
import SettingInterfaceLogistics from '@/pages/mall/admin/setting/interface/logistics.uvue'
|
||||||
|
import SettingInterfaceESheet from '@/pages/mall/admin/setting/interface/e-sheet.uvue'
|
||||||
|
import SettingInterfaceSms from '@/pages/mall/admin/setting/interface/sms.uvue'
|
||||||
|
import SettingInterfacePayment from '@/pages/mall/admin/setting/interface/payment.uvue'
|
||||||
|
|
||||||
|
// --- 分销模块 ---
|
||||||
|
import DistributionPromoter from '@/pages/mall/admin/distribution/promoter/index.uvue'
|
||||||
|
import DistributionLevel from '@/pages/mall/admin/distribution/level/index.uvue'
|
||||||
|
import DistributionSetting from '@/pages/mall/admin/distribution/setting/index.uvue'
|
||||||
|
import DivisionList from '@/pages/mall/admin/distribution/division/list.uvue'
|
||||||
|
import DivisionAgent from '@/pages/mall/admin/distribution/division/agent.uvue'
|
||||||
|
import DivisionApply from '@/pages/mall/admin/distribution/division/apply.uvue'
|
||||||
|
|
||||||
|
// --- 客服模块 ---
|
||||||
|
import KefuList from '@/pages/mall/admin/kefu/list.uvue'
|
||||||
|
import KefuWords from '@/pages/mall/admin/kefu/words.uvue'
|
||||||
|
import KefuFeedback from '@/pages/mall/admin/kefu/feedback.uvue'
|
||||||
|
import KefuAutoReply from '@/pages/mall/admin/kefu/auto_reply.uvue'
|
||||||
|
import KefuConfig from '@/pages/mall/admin/kefu/config.uvue'
|
||||||
|
|
||||||
|
// --- 装修模块 ---
|
||||||
|
import DecorationHome from '@/pages/mall/admin/decoration/home.uvue'
|
||||||
|
import DecorationCategory from '@/pages/mall/admin/decoration/category.uvue'
|
||||||
|
import DecorationUser from '@/pages/mall/admin/decoration/user.uvue'
|
||||||
|
import DecorationData from '@/pages/mall/admin/decoration/data-config.uvue'
|
||||||
|
import DecorationStyle from '@/pages/mall/admin/design/theme-style.uvue'
|
||||||
|
import DecorationMaterial from '@/pages/mall/admin/design/material.uvue'
|
||||||
|
import DecorationLink from '@/pages/mall/admin/design/link-management.uvue'
|
||||||
|
|
||||||
|
// --- 应用模块 ---
|
||||||
|
import AppWechatMenu from '@/pages/mall/admin/app/wechat/menu.uvue'
|
||||||
|
import AppWechatNews from '@/pages/mall/admin/app/wechat/news.uvue'
|
||||||
|
import AppWechatReplyFollow from '@/pages/mall/admin/app/wechat/reply/follow.uvue'
|
||||||
|
import AppWechatReplyKeyword from '@/pages/mall/admin/app/wechat/reply/keyword.uvue'
|
||||||
|
import AppWechatReplyInvalid from '@/pages/mall/admin/app/wechat/reply/invalid.uvue'
|
||||||
|
import AppWechatConfig from '@/pages/mall/admin/app/wechat/config.uvue'
|
||||||
|
import AppRoutineDownload from '@/pages/mall/admin/app/routine/download.uvue'
|
||||||
|
import AppRoutineConfig from '@/pages/mall/admin/app/routine/config.uvue'
|
||||||
|
import AppMobileConfig from '@/pages/mall/admin/app/mobile/config.uvue'
|
||||||
|
import AppMobileVersion from '@/pages/mall/admin/app/mobile/version.uvue'
|
||||||
|
import AppPcDesign from '@/pages/mall/admin/app/pc/design.uvue'
|
||||||
|
import AppPcConfig from '@/pages/mall/admin/app/pc/config.uvue'
|
||||||
|
|
||||||
|
// --- 维护模块 ---
|
||||||
|
import MaintainDevConfig from '@/pages/mall/admin/maintain/dev-config/category.uvue'
|
||||||
|
import MaintainDevData from '@/pages/mall/admin/maintain/dev-config/combination-data.uvue'
|
||||||
|
import MaintainDevTask from '@/pages/mall/admin/maintain/dev-config/cron-job.uvue'
|
||||||
|
import MaintainDevAuth from '@/pages/mall/admin/maintain/dev-config/permission.uvue'
|
||||||
|
import MaintainDevModule from '@/pages/mall/admin/maintain/dev-config/module-config.uvue'
|
||||||
|
import MaintainDevEvent from '@/pages/mall/admin/maintain/dev-config/custom-event.uvue'
|
||||||
|
import MaintainSecurityCache from '@/pages/mall/admin/maintain/security/refresh-cache.uvue'
|
||||||
|
import MaintainSecurityLog from '@/pages/mall/admin/maintain/security/system-log.uvue'
|
||||||
|
import MaintainSecurityUpgrade from '@/pages/mall/admin/maintain/security/online-upgrade.uvue'
|
||||||
|
import MaintainDataLogistics from '@/pages/mall/admin/maintain/data/logistics.uvue'
|
||||||
|
import MaintainDataCity from '@/pages/mall/admin/maintain/data/city.uvue'
|
||||||
|
import MaintainDataClear from '@/pages/mall/admin/maintain/data/clear.uvue'
|
||||||
|
import MaintainApiAccount from '@/pages/mall/admin/maintain/api/account.uvue'
|
||||||
|
import MaintainLangList from '@/pages/mall/admin/maintain/lang/list.uvue'
|
||||||
|
import MaintainLangDetail from '@/pages/mall/admin/maintain/lang/detail.uvue'
|
||||||
|
import MaintainLangRegion from '@/pages/mall/admin/maintain/lang/region.uvue'
|
||||||
|
import MaintainLangConfig from '@/pages/mall/admin/maintain/lang/config.uvue'
|
||||||
|
import MaintainToolDb from '@/pages/mall/admin/maintain/dev-tools/database.uvue'
|
||||||
|
import MaintainToolFile from '@/pages/mall/admin/maintain/dev-tools/file.uvue'
|
||||||
|
import MaintainToolApi from '@/pages/mall/admin/maintain/dev-tools/api.uvue'
|
||||||
|
import MaintainToolDic from '@/pages/mall/admin/maintain/dev-tools/data-dict.uvue'
|
||||||
|
import MaintainSysInfo from '@/pages/mall/admin/maintain/sys/info.uvue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件映射表
|
* 组件映射表
|
||||||
@@ -34,178 +181,167 @@ export const componentMap: Map<string, any> = new Map([
|
|||||||
['HomeIndex', HomeIndex],
|
['HomeIndex', HomeIndex],
|
||||||
|
|
||||||
// 用户模块
|
// 用户模块
|
||||||
['UserStatistic', defineAsyncComponent(() => import('@/pages/mall/admin/user/statistics/index.uvue'))],
|
['UserStatistic', UserStatistic],
|
||||||
['UserList', defineAsyncComponent(() => import('@/pages/mall/admin/user/management/index.uvue'))],
|
['UserList', UserList],
|
||||||
['UserLevel', defineAsyncComponent(() => import('@/pages/mall/admin/user/level/index.uvue'))],
|
['UserLevel', UserLevel],
|
||||||
['UserGroup', defineAsyncComponent(() => import('@/pages/mall/admin/user/grouping/index.uvue'))],
|
['UserGroup', UserGroup],
|
||||||
['UserLabel', defineAsyncComponent(() => import('@/pages/mall/admin/user/label/index.uvue'))],
|
['UserLabel', UserLabel],
|
||||||
['UserMemberConfig', defineAsyncComponent(() => import('@/pages/mall/admin/user/configuration/index.uvue'))],
|
['UserMemberConfig', UserMemberConfig],
|
||||||
|
|
||||||
// 商品模块
|
// 商品模块
|
||||||
['ProductStatistic', defineAsyncComponent(() => import('@/pages/mall/admin/product/product-statistics/index.uvue'))],
|
['ProductStatistic', ProductStatistic],
|
||||||
['ProductList', defineAsyncComponent(() => import('@/pages/mall/admin/product/product-management/index.uvue'))],
|
['ProductList', ProductList],
|
||||||
['ProductEdit', defineAsyncComponent(() => import('@/pages/mall/admin/product/product-management/edit.uvue'))],
|
['ProductEdit', ProductEdit],
|
||||||
['ProductMemberPrice', defineAsyncComponent(() => import('@/pages/mall/admin/product/product-management/member-price.uvue'))],
|
['ProductMemberPrice', ProductMemberPrice],
|
||||||
['ProductClassify', defineAsyncComponent(() => import('@/pages/mall/admin/product/classification/index.uvue'))],
|
['ProductClassify', ProductClassify],
|
||||||
['ProductReply', defineAsyncComponent(() => import('@/pages/mall/admin/product/reviews/index.uvue'))],
|
['ProductReply', ProductReply],
|
||||||
['ProductAttr', defineAsyncComponent(() => import('@/pages/mall/admin/product/specifications/index.uvue'))],
|
['ProductAttr', ProductAttr],
|
||||||
['ProductParam', defineAsyncComponent(() => import('@/pages/mall/admin/product/parameters/index.uvue'))],
|
['ProductParam', ProductParam],
|
||||||
['ProductLabel', defineAsyncComponent(() => import('@/pages/mall/admin/product/labels/index.uvue'))],
|
['ProductLabel', ProductLabel],
|
||||||
['ProductProtection', defineAsyncComponent(() => import('@/pages/mall/admin/product/protection/index.uvue'))],
|
['ProductProtection', ProductProtection],
|
||||||
|
|
||||||
// 订单模块
|
// 订单模块
|
||||||
['OrderList', defineAsyncComponent(() => import('@/pages/mall/admin/order/list.uvue'))],
|
['OrderList', OrderList],
|
||||||
['OrderStatistic', defineAsyncComponent(() => import('@/pages/mall/admin/order/order-statistics/index.uvue'))],
|
['OrderStatistic', OrderStatistic],
|
||||||
['OrderRefund', defineAsyncComponent(() => import('@/pages/mall/admin/order/aftersales-order/index.uvue'))],
|
['OrderRefund', OrderRefund],
|
||||||
['OrderCashier', defineAsyncComponent(() => import('@/pages/mall/admin/order/cashier-order/index.uvue'))],
|
['OrderCashier', OrderCashier],
|
||||||
['OrderVerify', defineAsyncComponent(() => import('@/pages/mall/admin/order/write-off-records/index.uvue'))],
|
['OrderVerify', OrderVerify],
|
||||||
['OrderConfig', defineAsyncComponent(() => import('@/pages/mall/admin/order/order-configuration/index.uvue'))],
|
['OrderConfig', OrderConfig],
|
||||||
|
|
||||||
// 营销模块已改为异步加载
|
// 营销模块
|
||||||
// 1. 优惠券
|
['MarketingCouponList', MarketingCouponList],
|
||||||
['MarketingCouponList', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/coupon/list.uvue'))],
|
['MarketingCouponUser', MarketingCouponUser],
|
||||||
['MarketingCouponUser', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/coupon/user.uvue'))],
|
['MarketingIntegralStatistic', MarketingIntegralStatistic],
|
||||||
// 2. 积分管理
|
['MarketingIntegralProduct', MarketingIntegralProduct],
|
||||||
['MarketingIntegralStatistic', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/integral/statistic.uvue'))],
|
['MarketingIntegralOrder', MarketingIntegralOrder],
|
||||||
['MarketingIntegralProduct', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/integral/list.uvue'))],
|
['MarketingIntegralRecord', MarketingIntegralRecord],
|
||||||
['MarketingIntegralOrder', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/integral/order.uvue'))],
|
['MarketingIntegralConfig', MarketingIntegralConfig],
|
||||||
['MarketingIntegralRecord', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/integral/record.uvue'))],
|
['MarketingLotteryList', MarketingLotteryList],
|
||||||
['MarketingIntegralConfig', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/integral/config.uvue'))],
|
['MarketingLotteryConfig', MarketingLotteryConfig],
|
||||||
// 3. 抽奖管理
|
|
||||||
['MarketingLotteryList', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/lottery/list.uvue'))],
|
|
||||||
['MarketingLotteryConfig', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/lottery/config.uvue'))],
|
|
||||||
// 4. 砍价管理
|
|
||||||
['MarketingBargainProduct', PlaceholderPage],
|
['MarketingBargainProduct', PlaceholderPage],
|
||||||
['MarketingBargainList', PlaceholderPage],
|
['MarketingBargainList', PlaceholderPage],
|
||||||
// 5. 拼团管理
|
['MarketingCombinationProduct', MarketingCombinationProduct],
|
||||||
['MarketingCombinationProduct', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/combination/product.uvue'))],
|
['MarketingCombinationList', MarketingCombinationList],
|
||||||
['MarketingCombinationList', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/combination/list.uvue'))],
|
['MarketingCombinationCreate', MarketingCombinationCreate],
|
||||||
['MarketingCombinationCreate', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/combination/create.uvue'))],
|
['MarketingSeckillList', MarketingSeckillList],
|
||||||
// 6. 秒杀管理
|
['MarketingSeckillProduct', MarketingSeckillProduct],
|
||||||
['MarketingSeckillList', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/seckill/list.uvue'))],
|
['MarketingSeckillConfig', MarketingSeckillConfig],
|
||||||
['MarketingSeckillProduct', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/seckill/product.uvue'))],
|
['MarketingMemberType', MarketingMemberType],
|
||||||
['MarketingSeckillConfig', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/seckill/config.uvue'))],
|
['MarketingMemberRight', MarketingMemberRight],
|
||||||
// 7. 付费会员
|
['MarketingMemberCard', MarketingMemberCard],
|
||||||
['MarketingMemberType', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/member/type.uvue'))],
|
['MarketingMemberRecord', MarketingMemberRecord],
|
||||||
['MarketingMemberRight', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/member/right.uvue'))],
|
['MarketingMemberConfig', MarketingMemberConfig],
|
||||||
['MarketingMemberCard', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/member/card.uvue'))],
|
['MarketingLiveRoom', MarketingLiveRoom],
|
||||||
['MarketingMemberRecord', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/member/record.uvue'))],
|
['MarketingLiveProduct', MarketingLiveProduct],
|
||||||
['MarketingMemberConfig', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/member/config.uvue'))],
|
['MarketingLiveAnchor', MarketingLiveAnchor],
|
||||||
// 8. 直播管理
|
['MarketingRechargeQuota', MarketingRechargeQuota],
|
||||||
['MarketingLiveRoom', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/live/room.uvue'))],
|
['MarketingRechargeConfig', MarketingRechargeConfig],
|
||||||
['MarketingLiveProduct', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/live/product.uvue'))],
|
['MarketingCheckinConfig', MarketingCheckinConfig],
|
||||||
['MarketingLiveAnchor', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/live/anchor.uvue'))],
|
['MarketingCheckinReward', MarketingCheckinReward],
|
||||||
// 9. 用户充值
|
|
||||||
['MarketingRechargeQuota', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/recharge/quota.uvue'))],
|
|
||||||
['MarketingRechargeConfig', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/recharge/config.uvue'))],
|
|
||||||
// 10. 每日签到
|
|
||||||
['MarketingCheckinConfig', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/checkin/config.uvue'))],
|
|
||||||
['MarketingCheckinReward', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/checkin/reward.uvue'))],
|
|
||||||
// 11. 渠道码 & 新人礼
|
|
||||||
['MarketingChannelList', PlaceholderPage],
|
['MarketingChannelList', PlaceholderPage],
|
||||||
['MarketingNewcomerGift', defineAsyncComponent(() => import('@/pages/mall/admin/marketing/newcomer/index.uvue'))],
|
['MarketingNewcomerGift', MarketingNewcomerGift],
|
||||||
|
|
||||||
// 内容模块
|
// 内容模块
|
||||||
['CmsArticle', defineAsyncComponent(() => import('@/pages/mall/admin/cms/article/list.uvue'))],
|
['CmsArticle', CmsArticle],
|
||||||
['CmsCategory', defineAsyncComponent(() => import('@/pages/mall/admin/cms/category/list.uvue'))],
|
['CmsCategory', CmsCategory],
|
||||||
|
|
||||||
// 财务模块
|
// 财务模块
|
||||||
['FinanceTransactionStats', defineAsyncComponent(() => import('@/pages/mall/admin/finance/transaction_stats.uvue'))],
|
['FinanceTransactionStats', FinanceTransactionStats],
|
||||||
['FinanceWithdrawal', defineAsyncComponent(() => import('@/pages/mall/admin/finance/withdrawal.uvue'))],
|
['FinanceWithdrawal', FinanceWithdrawal],
|
||||||
['FinanceInvoice', defineAsyncComponent(() => import('@/pages/mall/admin/finance/invoice.uvue'))],
|
['FinanceInvoice', FinanceInvoice],
|
||||||
['FinanceRecharge', defineAsyncComponent(() => import('@/pages/mall/admin/finance/recharge.uvue'))],
|
['FinanceRecharge', FinanceRecharge],
|
||||||
['FinanceCapitalFlow', defineAsyncComponent(() => import('@/pages/mall/admin/finance/capital_flow.uvue'))],
|
['FinanceCapitalFlow', FinanceCapitalFlow],
|
||||||
['FinanceBill', defineAsyncComponent(() => import('@/pages/mall/admin/finance/bill.uvue'))],
|
['FinanceBill', FinanceBill],
|
||||||
['FinanceCommission', defineAsyncComponent(() => import('@/pages/mall/admin/finance/commission.uvue'))],
|
['FinanceCommission', FinanceCommission],
|
||||||
['FinanceBalanceStats', defineAsyncComponent(() => import('@/pages/mall/admin/finance/balance_stats.uvue'))],
|
['FinanceBalanceStats', FinanceBalanceStats],
|
||||||
['FinanceBalanceRecord', defineAsyncComponent(() => import('@/pages/mall/admin/finance/balance_record.uvue'))],
|
['FinanceBalanceRecord', FinanceBalanceRecord],
|
||||||
|
|
||||||
// 数据模块 - 暂时使用占位组件
|
// 数据模块
|
||||||
['StatisticIndex', PlaceholderPage],
|
['StatisticIndex', PlaceholderPage],
|
||||||
|
|
||||||
// 设置模块
|
// 设置模块
|
||||||
['SettingSystemConfig', defineAsyncComponent(() => import('@/pages/mall/admin/setting/system/config.uvue'))],
|
['SettingSystemConfig', SettingSystemConfig],
|
||||||
['SettingMessageIndex', defineAsyncComponent(() => import('@/pages/mall/admin/setting/message.uvue'))],
|
['SettingMessageIndex', SettingMessageIndex],
|
||||||
['SettingProtocolIndex', defineAsyncComponent(() => import('@/pages/mall/admin/setting/agreement.uvue'))],
|
['SettingProtocolIndex', SettingProtocolIndex],
|
||||||
['SettingTicketIndex', defineAsyncComponent(() => import('@/pages/mall/admin/setting/ticket.uvue'))],
|
['SettingTicketIndex', SettingTicketIndex],
|
||||||
['SettingAuthRole', defineAsyncComponent(() => import('@/pages/mall/admin/setting/auth/role.uvue'))],
|
['SettingAuthRole', SettingAuthRole],
|
||||||
['SettingAuthAdmin', defineAsyncComponent(() => import('@/pages/mall/admin/setting/auth/admin.uvue'))],
|
['SettingAuthAdmin', SettingAuthAdmin],
|
||||||
['SettingAuthPermission', defineAsyncComponent(() => import('@/pages/mall/admin/setting/auth/permission.uvue'))],
|
['SettingAuthPermission', SettingAuthPermission],
|
||||||
['SettingDeliveryStaff', defineAsyncComponent(() => import('@/pages/mall/admin/setting/delivery/staff.uvue'))],
|
['SettingDeliveryStaff', SettingDeliveryStaff],
|
||||||
['SettingDeliveryStation', defineAsyncComponent(() => import('@/pages/mall/admin/setting/delivery/station.uvue'))],
|
['SettingDeliveryStation', SettingDeliveryStation],
|
||||||
['SettingDeliveryVerifier', defineAsyncComponent(() => import('@/pages/mall/admin/setting/delivery/verifier.uvue'))],
|
['SettingDeliveryVerifier', SettingDeliveryVerifier],
|
||||||
['SettingDeliveryTemplate', defineAsyncComponent(() => import('@/pages/mall/admin/setting/delivery/template.uvue'))],
|
['SettingDeliveryTemplate', SettingDeliveryTemplate],
|
||||||
['SettingInterfaceOnepassConfig', defineAsyncComponent(() => import('@/pages/mall/admin/setting/interface/onepass/config.uvue'))],
|
['SettingInterfaceOnepassConfig', SettingInterfaceOnepassConfig],
|
||||||
['SettingInterfaceOnepassIndex', defineAsyncComponent(() => import('@/pages/mall/admin/setting/interface/onepass/index.uvue'))],
|
['SettingInterfaceOnepassIndex', SettingInterfaceOnepassIndex],
|
||||||
['SettingInterfaceStorage', defineAsyncComponent(() => import('@/pages/mall/admin/setting/interface/storage.uvue'))],
|
['SettingInterfaceStorage', SettingInterfaceStorage],
|
||||||
['SettingInterfaceCollect', defineAsyncComponent(() => import('@/pages/mall/admin/setting/interface/collect.uvue'))],
|
['SettingInterfaceCollect', SettingInterfaceCollect],
|
||||||
['SettingInterfaceLogistics', defineAsyncComponent(() => import('@/pages/mall/admin/setting/interface/logistics.uvue'))],
|
['SettingInterfaceLogistics', SettingInterfaceLogistics],
|
||||||
['SettingInterfaceESheet', defineAsyncComponent(() => import('@/pages/mall/admin/setting/interface/e-sheet.uvue'))],
|
['SettingInterfaceESheet', SettingInterfaceESheet],
|
||||||
['SettingInterfaceSms', defineAsyncComponent(() => import('@/pages/mall/admin/setting/interface/sms.uvue'))],
|
['SettingInterfaceSms', SettingInterfaceSms],
|
||||||
['SettingInterfacePayment', defineAsyncComponent(() => import('@/pages/mall/admin/setting/interface/payment.uvue'))],
|
['SettingInterfacePayment', SettingInterfacePayment],
|
||||||
|
|
||||||
// 分销模块
|
// 分销模块
|
||||||
['DistributionStatistic', PlaceholderPage],
|
['DistributionStatistic', PlaceholderPage],
|
||||||
['DistributionPromoter', defineAsyncComponent(() => import('@/pages/mall/admin/distribution/promoter/index.uvue'))],
|
['DistributionPromoter', DistributionPromoter],
|
||||||
['DistributionLevel', defineAsyncComponent(() => import('@/pages/mall/admin/distribution/level/index.uvue'))],
|
['DistributionLevel', DistributionLevel],
|
||||||
['DistributionSetting', defineAsyncComponent(() => import('@/pages/mall/admin/distribution/setting/index.uvue'))],
|
['DistributionSetting', DistributionSetting],
|
||||||
['DivisionList', defineAsyncComponent(() => import('@/pages/mall/admin/distribution/division/list.uvue'))],
|
['DivisionList', DivisionList],
|
||||||
['DivisionAgent', defineAsyncComponent(() => import('@/pages/mall/admin/distribution/division/agent.uvue'))],
|
['DivisionAgent', DivisionAgent],
|
||||||
['DivisionApply', defineAsyncComponent(() => import('@/pages/mall/admin/distribution/division/apply.uvue'))],
|
['DivisionApply', DivisionApply],
|
||||||
|
|
||||||
// 客服模块
|
// 客服模块
|
||||||
['KefuList', defineAsyncComponent(() => import('@/pages/mall/admin/kefu/list.uvue'))],
|
['KefuList', KefuList],
|
||||||
['KefuWords', defineAsyncComponent(() => import('@/pages/mall/admin/kefu/words.uvue'))],
|
['KefuWords', KefuWords],
|
||||||
['KefuFeedback', defineAsyncComponent(() => import('@/pages/mall/admin/kefu/feedback.uvue'))],
|
['KefuFeedback', KefuFeedback],
|
||||||
['KefuAutoReply', defineAsyncComponent(() => import('@/pages/mall/admin/kefu/auto_reply.uvue'))],
|
['KefuAutoReply', KefuAutoReply],
|
||||||
['KefuConfig', defineAsyncComponent(() => import('@/pages/mall/admin/kefu/config.uvue'))],
|
['KefuConfig', KefuConfig],
|
||||||
|
|
||||||
// 装修模块
|
// 装修模块
|
||||||
['DecorationHome', defineAsyncComponent(() => import('@/pages/mall/admin/decoration/home.uvue'))],
|
['DecorationHome', DecorationHome],
|
||||||
['DecorationCategory', defineAsyncComponent(() => import('@/pages/mall/admin/decoration/category.uvue'))],
|
['DecorationCategory', DecorationCategory],
|
||||||
['DecorationUser', defineAsyncComponent(() => import('@/pages/mall/admin/decoration/user.uvue'))],
|
['DecorationUser', DecorationUser],
|
||||||
['DecorationData', defineAsyncComponent(() => import('@/pages/mall/admin/decoration/data-config.uvue'))],
|
['DecorationData', DecorationData],
|
||||||
['DecorationStyle', defineAsyncComponent(() => import('@/pages/mall/admin/design/theme-style.uvue'))],
|
['DecorationStyle', DecorationStyle],
|
||||||
['DecorationMaterial', defineAsyncComponent(() => import('@/pages/mall/admin/design/material.uvue'))],
|
['DecorationMaterial', DecorationMaterial],
|
||||||
['DecorationLink', defineAsyncComponent(() => import('@/pages/mall/admin/design/link-management.uvue'))],
|
['DecorationLink', DecorationLink],
|
||||||
|
|
||||||
// 应用模块
|
// 应用模块
|
||||||
['AppWechatMenu', defineAsyncComponent(() => import('@/pages/mall/admin/app/wechat/menu.uvue'))],
|
['AppWechatMenu', AppWechatMenu],
|
||||||
['AppWechatNews', defineAsyncComponent(() => import('@/pages/mall/admin/app/wechat/news.uvue'))],
|
['AppWechatNews', AppWechatNews],
|
||||||
['AppWechatReplyFollow', defineAsyncComponent(() => import('@/pages/mall/admin/app/wechat/reply/follow.uvue'))],
|
['AppWechatReplyFollow', AppWechatReplyFollow],
|
||||||
['AppWechatReplyKeyword', defineAsyncComponent(() => import('@/pages/mall/admin/app/wechat/reply/keyword.uvue'))],
|
['AppWechatReplyKeyword', AppWechatReplyKeyword],
|
||||||
['AppWechatReplyInvalid', defineAsyncComponent(() => import('@/pages/mall/admin/app/wechat/reply/invalid.uvue'))],
|
['AppWechatReplyInvalid', AppWechatReplyInvalid],
|
||||||
['AppWechatConfig', defineAsyncComponent(() => import('@/pages/mall/admin/app/wechat/config.uvue'))],
|
['AppWechatConfig', AppWechatConfig],
|
||||||
['AppRoutineDownload', defineAsyncComponent(() => import('@/pages/mall/admin/app/routine/download.uvue'))],
|
['AppRoutineDownload', AppRoutineDownload],
|
||||||
['AppRoutineConfig', defineAsyncComponent(() => import('@/pages/mall/admin/app/routine/config.uvue'))],
|
['AppRoutineConfig', AppRoutineConfig],
|
||||||
['AppMobileConfig', defineAsyncComponent(() => import('@/pages/mall/admin/app/mobile/config.uvue'))],
|
['AppMobileConfig', AppMobileConfig],
|
||||||
['AppMobileVersion', defineAsyncComponent(() => import('@/pages/mall/admin/app/mobile/version.uvue'))],
|
['AppMobileVersion', AppMobileVersion],
|
||||||
['AppPcDesign', defineAsyncComponent(() => import('@/pages/mall/admin/app/pc/design.uvue'))],
|
['AppPcDesign', AppPcDesign],
|
||||||
['AppPcConfig', defineAsyncComponent(() => import('@/pages/mall/admin/app/pc/config.uvue'))],
|
['AppPcConfig', AppPcConfig],
|
||||||
|
|
||||||
// 维护模块
|
// 维护模块
|
||||||
['MaintainDevConfig', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/category.uvue'))],
|
['MaintainDevConfig', MaintainDevConfig],
|
||||||
['MaintainDevData', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/combination-data.uvue'))],
|
['MaintainDevData', MaintainDevData],
|
||||||
['MaintainDevTask', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/cron-job.uvue'))],
|
['MaintainDevTask', MaintainDevTask],
|
||||||
['MaintainDevAuth', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/permission.uvue'))],
|
['MaintainDevAuth', MaintainDevAuth],
|
||||||
['MaintainDevModule', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/module-config.uvue'))],
|
['MaintainDevModule', MaintainDevModule],
|
||||||
['MaintainDevEvent', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/custom-event.uvue'))],
|
['MaintainDevEvent', MaintainDevEvent],
|
||||||
['MaintainSecurityCache', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/security/refresh-cache.uvue'))],
|
['MaintainSecurityCache', MaintainSecurityCache],
|
||||||
['MaintainSecurityLog', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/security/system-log.uvue'))],
|
['MaintainSecurityLog', MaintainSecurityLog],
|
||||||
['MaintainSecurityUpgrade', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/security/online-upgrade.uvue'))],
|
['MaintainSecurityUpgrade', MaintainSecurityUpgrade],
|
||||||
['MaintainDataLogistics', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/data/logistics.uvue'))],
|
['MaintainDataLogistics', MaintainDataLogistics],
|
||||||
['MaintainDataCity', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/data/city.uvue'))],
|
['MaintainDataCity', MaintainDataCity],
|
||||||
['MaintainDataClear', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/data/clear.uvue'))],
|
['MaintainDataClear', MaintainDataClear],
|
||||||
['MaintainApiAccount', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/api/account.uvue'))],
|
['MaintainApiAccount', MaintainApiAccount],
|
||||||
['MaintainLangList', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/lang/list.uvue'))],
|
['MaintainLangList', MaintainLangList],
|
||||||
['MaintainLangDetail', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/lang/detail.uvue'))],
|
['MaintainLangDetail', MaintainLangDetail],
|
||||||
['MaintainLangRegion', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/lang/region.uvue'))],
|
['MaintainLangRegion', MaintainLangRegion],
|
||||||
['MaintainLangConfig', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/lang/config.uvue'))],
|
['MaintainLangConfig', MaintainLangConfig],
|
||||||
['MaintainToolDb', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-tools/database.uvue'))],
|
['MaintainToolDb', MaintainToolDb],
|
||||||
['MaintainToolFile', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-tools/file.uvue'))],
|
['MaintainToolFile', MaintainToolFile],
|
||||||
['MaintainToolApi', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-tools/api.uvue'))],
|
['MaintainToolApi', MaintainToolApi],
|
||||||
['MaintainToolDic', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-tools/data-dict.uvue'))],
|
['MaintainToolDic', MaintainToolDic],
|
||||||
['MaintainSysInfo', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/sys/info.uvue'))]
|
['MaintainSysInfo', MaintainSysInfo]
|
||||||
])
|
])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -119,10 +119,11 @@ export const topMenus: TopMenu[] = [
|
|||||||
id: 'distribution',
|
id: 'distribution',
|
||||||
title: '分销',
|
title: '分销',
|
||||||
icon: 'share',
|
icon: 'share',
|
||||||
path: '/pages/mall/admin/distribution/statistic',
|
path: '/pages/mall/admin/distribution/promoter/index',
|
||||||
order: 6,
|
order: 6,
|
||||||
groups: [
|
groups: [
|
||||||
{ id: 'distribution-manage', title: '', order: 1 }
|
{ id: 'distribution-manage', title: '', order: 1 },
|
||||||
|
{ id: 'distribution-division', title: '事业部', order: 2 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -905,6 +906,7 @@ export const routes: RouteRecord[] = [
|
|||||||
componentKey: 'DistributionStatistic',
|
componentKey: 'DistributionStatistic',
|
||||||
parentId: 'distribution',
|
parentId: 'distribution',
|
||||||
groupId: 'distribution-manage',
|
groupId: 'distribution-manage',
|
||||||
|
hidden: true,
|
||||||
order: 1
|
order: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -936,30 +938,30 @@ export const routes: RouteRecord[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'division_list',
|
id: 'division_list',
|
||||||
title: '事业部管理',
|
title: '事业部列表',
|
||||||
path: '/pages/mall/admin/distribution/division/list',
|
path: '/pages/mall/admin/distribution/division/list',
|
||||||
componentKey: 'DivisionList',
|
componentKey: 'DivisionList',
|
||||||
parentId: 'distribution',
|
parentId: 'distribution',
|
||||||
groupId: 'distribution-manage',
|
groupId: 'distribution-division',
|
||||||
order: 5
|
order: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'division_agent',
|
id: 'division_agent',
|
||||||
title: '代理商管理',
|
title: '代理商列表',
|
||||||
path: '/pages/mall/admin/distribution/division/agent',
|
path: '/pages/mall/admin/distribution/division/agent',
|
||||||
componentKey: 'DivisionAgent',
|
componentKey: 'DivisionAgent',
|
||||||
parentId: 'distribution',
|
parentId: 'distribution',
|
||||||
groupId: 'distribution-manage',
|
groupId: 'distribution-division',
|
||||||
order: 6
|
order: 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'division_apply',
|
id: 'division_apply',
|
||||||
title: '事业部申请',
|
title: '代理商申请',
|
||||||
path: '/pages/mall/admin/distribution/division/apply',
|
path: '/pages/mall/admin/distribution/division/apply',
|
||||||
componentKey: 'DivisionApply',
|
componentKey: 'DivisionApply',
|
||||||
parentId: 'distribution',
|
parentId: 'distribution',
|
||||||
groupId: 'distribution-manage',
|
groupId: 'distribution-division',
|
||||||
order: 7
|
order: 3
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========== 客服模块 ==========
|
// ========== 客服模块 ==========
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ export type MenuNode = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const settingSubSiderMenu: MenuNode[] = [
|
export const settingSubSiderMenu: MenuNode[] = [
|
||||||
|
{ id: 'system_setting_group', title: '系统设置', type: 'group', children: [
|
||||||
|
{ id: 'setting_system_config', title: '系统配置', type: 'page', path: '/pages/mall/admin/setting/system/config' }
|
||||||
|
]
|
||||||
|
},
|
||||||
{ id: 'setting_message_index', title: '消息管理', type: 'page', path: '/pages/mall/admin/setting/message' },
|
{ id: 'setting_message_index', title: '消息管理', type: 'page', path: '/pages/mall/admin/setting/message' },
|
||||||
{ id: 'setting_protocol_index', title: '协议设置', type: 'page', path: '/pages/mall/admin/setting/agreement' },
|
{ id: 'setting_protocol_index', title: '协议设置', type: 'page', path: '/pages/mall/admin/setting/agreement' },
|
||||||
{ id: 'setting_ticket_index', title: '小票配置', type: 'page', path: '/pages/mall/admin/setting/ticket' },
|
{ id: 'setting_ticket_index', title: '小票配置', type: 'page', path: '/pages/mall/admin/setting/ticket' },
|
||||||
|
|||||||
14
pages.json
14
pages.json
@@ -843,6 +843,20 @@
|
|||||||
"navigationBarTitleText": "PC端配置",
|
"navigationBarTitleText": "PC端配置",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "maintain/dev-config/category",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "配置分类",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "setting/system/config",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "系统配置",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ const handleDelete = (item: VersionItem) => {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-bar {
|
.action-bar {
|
||||||
|
|||||||
@@ -125,7 +125,8 @@ const handleSubmit = () => {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card {
|
.content-card {
|
||||||
|
|||||||
@@ -196,7 +196,8 @@ function handleSubmit() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card {
|
.content-card {
|
||||||
|
|||||||
@@ -119,6 +119,37 @@
|
|||||||
- `chart-col` 在桌面端 (>=1200) 使用中等高度 (约 320-360px)。
|
- `chart-col` 在桌面端 (>=1200) 使用中等高度 (约 320-360px)。
|
||||||
- 在移动端/窄屏 (<1200) 自动扩展为大高度 (约 500-600px),以匹配全宽展示的视觉张力。
|
- 在移动端/窄屏 (<1200) 自动扩展为大高度 (约 500-600px),以匹配全宽展示的视觉张力。
|
||||||
|
|
||||||
|
#### **原因十九:布局组件依赖缺失导致的级联加载失败**
|
||||||
|
|
||||||
|
- **现象**: 控制台报 `GET http://.../AdminLayout.uvue?import net::ERR_CACHE_READ_FAILURE` 或 `TypeError: Failed to fetch dynamically imported module`。
|
||||||
|
- **原因**: 核心布局组件(如 `AdminLayout.uvue`)在 `<script setup>` 中使用了 `watch`、`computed` 或其他 Vue API 但**未在顶部 import**。这会导致 JavaScript 语法解析错误,使整个模块加载失败。由于它是所有页面的父容器,会导致全站白屏且报错信息具有误导性(看似网络错误,实为语法错误)。
|
||||||
|
- **解决方案**:
|
||||||
|
1. 检查所有在 `setup` 块中使用的 Vue API 是否已显式导入:`import { ref, computed, watch, onMounted } from 'vue'`。
|
||||||
|
2. 使用浏览器的 Network 面板查看失败的 `.uvue?import` 请求详情,查看看具体的语法错误堆栈。
|
||||||
|
|
||||||
|
#### **原因二十:UVUE 组件导入路径不规范与生命周期误用**
|
||||||
|
|
||||||
|
- **现象**: 页面显示正常但控制台抛出 `Unhandled error during execution of async component loader` 或 `onLoad is not defined`。
|
||||||
|
- **原因**:
|
||||||
|
1. **路径缺失后缀**: 在 UVUE 全局构建环境下,导入自定义 `.uvue` 组件必须显式包含 `.uvue` 后缀。
|
||||||
|
2. **生命周期冲突**: 在 `<script setup>` 语法糖中直接编写 `onLoad(() => {})` 而未从 `@dcloudio/uni-app` 导入。
|
||||||
|
- **解决方案**:
|
||||||
|
1. **强制后缀**: `import AdminLayout from '@/layouts/admin/AdminLayout.uvue'`。
|
||||||
|
2. **规范生命周期**:
|
||||||
|
- 简单初始化建议统一使用 Vue 标准的 `onMounted(() => {})`。
|
||||||
|
- 如需获取页面参数,通过 `getCurrentPages()` 获取当前页面实例的 `options`。
|
||||||
|
- 禁止在 `setup` 顶层直接定义未声明的 `onLoad/onShow`。
|
||||||
|
|
||||||
|
#### **原因二十一:Admin 内部路由 DYNAMIC IMPORT 兼容性问题**
|
||||||
|
|
||||||
|
- **现象**: 在 Admin 后台切换菜单或打开特定维护页面时,控制台抛出 `TypeError: Failed to fetch dynamically imported module` 或 `ERR_CACHE_READ_FAILURE`。
|
||||||
|
- **原因**:
|
||||||
|
1. **环境限制**: 在某些 `uni-app-x` 的 H5 Vite 编译环境下,针对 `.uvue` 文件的动态 `import()` 支持可能存在不稳定性。
|
||||||
|
2. **语法敏感**: 如果被异步加载的组件本身存在轻微的 UTS/Composition API 语法错误,Vite 的 H5 运行时可能无法正确捕获并提示,而是直接抛出网络加载失败相关的错误。
|
||||||
|
- **解决方案**:
|
||||||
|
1. **转为静态导入**: 在 `adminComponentMap.uts` 顶部使用静态 `import` 导入所有管理端子页面组件。
|
||||||
|
2. **组件映射**: 维护 `componentMap` 为静态 Map,避免在运行时使用 `defineAsyncComponent`,从而提高页面的加载成功率和抗语法错误风险。
|
||||||
|
|
||||||
## 🛠️ 完整修复流程
|
## 🛠️ 完整修复流程
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -1862,4 +1893,25 @@ const iconMap: Record<string, string> = {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
这个指南现在涵盖了 uni-app-x 项目开发中最常见的 15 类问题(新增 CRMEB 路由体系复刻),为后续开发提供了完整的故障排除和最佳实践指导。 🚀
|
这个指南现在涵盖了 uni-app-x 项目开发中最常见的 17 类问题(新增动态导入与语法遮蔽解析),为后续开发提供了完整的故障排除和最佳实践指导。 🚀
|
||||||
|
|
||||||
|
### 原因二十一:动态导入 (Dynamic Import) 导致 H5 加载异常 (net::ERR_CACHE_READ_FAILURE)
|
||||||
|
|
||||||
|
**问题描述:**
|
||||||
|
在 H5 环境下,使用 `defineAsyncComponent` 或 Vite 默认的动态导入语法加载 `.uvue` 组件时,经常出现 `net::ERR_CACHE_READ_FAILURE` 错误。这通常是因为 UTS 编译器在处理动态分包时,无法正确生成或缓存对应的脚本模块。
|
||||||
|
|
||||||
|
**解决方案:**
|
||||||
|
|
||||||
|
1. **强制改为静态导入**:在路由配置文件(如 `adminComponentMap.uts`)中,不要使用 `() => import(...)`,而是直接使用顶层的 `import` 语句导入所有组件。
|
||||||
|
2. **中心化映射表**:建立一个中心化的组件映射表,利用 UTS 的强类型特性确保组件在编译期就被分析和包含。
|
||||||
|
|
||||||
|
### 原因二十二:语法错误导致模块加载失败 (Masked Syntax Errors)
|
||||||
|
|
||||||
|
**问题描述:**
|
||||||
|
某些 UTS 语法错误(如非标准的泛型写法 `reactive<T>` 或不兼容的脚本标签 `<script uts>`)在 H5 模式下不会直接报出详细的语法错误,而是会导致生成的 JS 模块无效,从而触发浏览器的 `net::ERR_CACHE_READ_FAILURE`。
|
||||||
|
|
||||||
|
**解决方案:**
|
||||||
|
|
||||||
|
1. **标准化 Script 标签**:统一使用 `<script setup lang="uts">` 或 `<script lang="uts">`(配合 `defineComponent`)。
|
||||||
|
2. **规范响应式声明**:避免在 `reactive` 上直接使用泛型(如 `reactive<T>(...)`),应使用类型断言 `reactive(...) as T`。
|
||||||
|
3. **避免遗留的 Options API 写法**:尽量将旧的 `export default { data(), methods() }` 结构转换为 Composition API 模式,以获得最佳的 UTS 编译支持。
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="admin-home-page">
|
<view class="admin-home-page">
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<view class="home-content">
|
|
||||||
<text class="welcome-text">管理后台首页</text>
|
|
||||||
</view>
|
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import AdminLayout from '@/layouts/admin/AdminLayout'
|
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||||
|
|
||||||
const title = ref<string>('管理后台首页')
|
const title = ref<string>('管理后台首页')
|
||||||
|
|
||||||
onLoad((options: OnLoadOptions) => {
|
onMounted(() => {
|
||||||
console.log('首页加载完成')
|
console.log('首页加载完成')
|
||||||
})
|
|
||||||
|
|
||||||
onShow(() => {
|
|
||||||
console.log('首页显示')
|
console.log('首页显示')
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -103,8 +103,7 @@ function deleteItem(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 24px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,8 +97,7 @@ function deleteItem(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ function handleAction(card: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 24px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,8 +104,7 @@ function editItem(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,268 +1,153 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="admin-page">
|
<AdminLayout current-page="maintain_dev_config">
|
||||||
<view class="admin-sections">
|
<view class="admin-page category-page">
|
||||||
<!-- 搜索栏 -->
|
<view class="admin-sections">
|
||||||
<view class="admin-card filter-card">
|
<!-- 筛选区域 (1:1 复刻 CRMEB: 横向排列的过滤器) -->
|
||||||
<view class="filter-row">
|
<view class="admin-card filter-card">
|
||||||
<view class="filter-item">
|
<view class="filter-row">
|
||||||
<text class="label">是否显示:</text>
|
<view class="filter-item">
|
||||||
<view class="filter-select">
|
<text class="label">是否显示:</text>
|
||||||
<text class="select-placeholder">请选择</text>
|
<view class="compact-select-wrapper">
|
||||||
<text class="arrow">▼</text>
|
<text class="select-text">请选择</text>
|
||||||
|
<text class="arrow">▼</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="filter-item">
|
||||||
|
<text class="label">分类名称:</text>
|
||||||
|
<input class="filter-input" placeholder="请输入分类名称" />
|
||||||
|
</view>
|
||||||
|
<button class="btn-search">查询分类</button>
|
||||||
</view>
|
</view>
|
||||||
<view class="filter-item">
|
|
||||||
<text class="label">分类名称:</text>
|
<view class="filter-row second-row">
|
||||||
<input class="filter-input" placeholder="请输入分类名称" />
|
<view class="filter-item">
|
||||||
|
<text class="label">配置名称:</text>
|
||||||
|
<input class="filter-input" placeholder="请输入配置名称" />
|
||||||
|
</view>
|
||||||
|
<button class="btn-search">查询配置</button>
|
||||||
</view>
|
</view>
|
||||||
<button class="btn primary" @click="onSearch">查询分类</button>
|
|
||||||
</view>
|
|
||||||
<view class="filter-row mt-12">
|
|
||||||
<view class="filter-item">
|
|
||||||
<text class="label">配置名称:</text>
|
|
||||||
<input class="filter-input" placeholder="请输入配置名称" />
|
|
||||||
</view>
|
|
||||||
<button class="btn primary" @click="onSearchConfig">查询配置</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 内容区 -->
|
|
||||||
<view class="admin-card content-card">
|
|
||||||
<!-- 操作按钮行 -->
|
|
||||||
<view class="action-bar">
|
|
||||||
<button class="btn primary small" @click="onAdd">添加配置分类</button>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格板块 -->
|
||||||
<view class="table-container list-table">
|
<view class="admin-card table-card">
|
||||||
<view class="table-header">
|
<view class="table-header-actions">
|
||||||
<view class="col col-id"><text>ID</text></view>
|
<view class="btn-add">添加配置分类</view>
|
||||||
<view class="col col-name"><text>分类名称</text></view>
|
|
||||||
<view class="col col-field"><text>分类字段</text></view>
|
|
||||||
<view class="col col-status"><text>状态</text></view>
|
|
||||||
<view class="col col-ops"><text>操作</text></view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="table-body">
|
<!-- 模拟表格 (CRMEB风格: 线条简约, 蓝调表头) -->
|
||||||
<view v-for="item in dataList" :key="item.id" class="table-row">
|
<view class="table-container">
|
||||||
<view class="col col-id"><text>{{ item.id }}</text></view>
|
<view class="table-thead">
|
||||||
<view class="col col-name">
|
<view class="th cell-id">ID</view>
|
||||||
<text class="expand-icon" v-if="item.hasChildren">▶</text>
|
<view class="th cell-name">分类名称</view>
|
||||||
<text>{{ item.name }}</text>
|
<view class="th cell-field">分类字段</view>
|
||||||
</view>
|
<view class="th cell-status">状态</view>
|
||||||
<view class="col col-field"><text>{{ item.field }}</text></view>
|
<view class="th cell-ops">操作</view>
|
||||||
<view class="col col-status">
|
</view>
|
||||||
<switch :checked="item.status" color="#1890ff" scale="0.8" />
|
|
||||||
</view>
|
<view class="table-tbody">
|
||||||
<view class="col col-ops">
|
<view v-for="item in categoryList" :key="item.id" class="tr">
|
||||||
<text class="op-link" @click="onConfig(item)">配置列表</text>
|
<view class="td cell-id">{{ item.id }}</view>
|
||||||
<view class="op-divider">|</view>
|
<view class="td cell-name">
|
||||||
<text class="op-link" @click="onEdit(item)">编辑</text>
|
<text class="tree-icon">▶</text>
|
||||||
<view class="op-divider">|</view>
|
<text>{{ item.name }}</text>
|
||||||
<text class="op-link op-danger" @click="onDelete(item)">删除</text>
|
</view>
|
||||||
|
<view class="td cell-field">{{ item.field }}</view>
|
||||||
|
<view class="td cell-status">
|
||||||
|
<view class="toggle-switch" :class="{ active: item.status }" @click="item.status = !item.status">
|
||||||
|
<view class="switch-ball"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="td cell-ops">
|
||||||
|
<text class="op-link">配置列表</text>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<text class="op-link">编辑</text>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<text class="op-link delete">删除</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||||
|
|
||||||
const dataList = ref([
|
const categoryList = ref([
|
||||||
{ id: 9, name: '分销配置', field: 'fenxiao', status: true, hasChildren: true },
|
{ id: 9, name: '分销配置', field: 'fenxiao', status: true },
|
||||||
{ id: 65, name: '接口设置', field: 'system_serve', status: true, hasChildren: true },
|
{ id: 65, name: '接口设置', field: 'system_serve', status: true },
|
||||||
{ id: 69, name: '客服配置', field: 'kefu_config', status: true, hasChildren: true },
|
{ id: 69, name: '客服配置', field: 'kefu_config', status: true },
|
||||||
{ id: 78, name: '应用配置', field: 'sys_app', status: true, hasChildren: true },
|
{ id: 78, name: '应用配置', field: 'sys_app', status: true },
|
||||||
{ id: 100, name: '用户配置', field: 'system_user_config', status: true, hasChildren: true },
|
{ id: 100, name: '用户配置', field: 'system_user_config', status: true },
|
||||||
{ id: 113, name: '订单配置', field: 'order_config', status: true, hasChildren: true },
|
{ id: 113, name: '订单配置', field: 'order_config', status: true },
|
||||||
{ id: 129, name: '系统配置', field: 'system_config', status: true, hasChildren: true },
|
{ id: 129, name: '系统配置', field: 'system_config', status: true },
|
||||||
{ id: 136, name: '商品配置', field: 'product_config', status: true, hasChildren: true }
|
{ id: 136, name: '商品配置', field: 'product_config', status: true }
|
||||||
])
|
])
|
||||||
|
|
||||||
function onSearch() {
|
|
||||||
uni.showToast({ title: '查询分类', icon: 'none' })
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSearchConfig() {
|
|
||||||
uni.showToast({ title: '查询配置', icon: 'none' })
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAdd() {
|
|
||||||
uni.showToast({ title: '添加配置分类', icon: 'none' })
|
|
||||||
}
|
|
||||||
|
|
||||||
function onConfig(item: any) {
|
|
||||||
uni.showToast({ title: '配置列表: ' + item.name, icon: 'none' })
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEdit(item: any) {
|
|
||||||
uni.showToast({ title: '编辑: ' + item.name, icon: 'none' })
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDelete(item: any) {
|
|
||||||
uni.showModal({
|
|
||||||
title: '提示',
|
|
||||||
content: '确定要删除该分类吗?',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
uni.showToast({ title: '已删除', icon: 'none' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped>
|
||||||
.admin-page {
|
.category-page { padding: 0; background-color: transparent; }
|
||||||
padding: 20px;
|
|
||||||
background-color: #f5f7f9;
|
.filter-card {
|
||||||
min-height: 100vh;
|
margin-bottom: var(--admin-section-gap);
|
||||||
|
padding: var(--admin-card-padding);
|
||||||
|
box-shadow: 0 1px 4px rgba(0,21,41,0.08);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.filter-row { display: flex; flex-direction: row; align-items: center; }
|
||||||
|
.second-row { margin-top: 15px; border-top: 1px dashed #eee; padding-top: 15px; }
|
||||||
|
|
||||||
|
.filter-item { display: flex; flex-direction: row; align-items: center; margin-right: 30px; }
|
||||||
|
.filter-item .label { font-size: 14px; color: #515a6e; margin-right: 10px; }
|
||||||
|
|
||||||
|
.compact-select-wrapper {
|
||||||
|
display: flex; flex-direction: row; align-items: center;
|
||||||
|
border: 1px solid #dcdee2; width: 180px; height: 32px;
|
||||||
|
padding: 0 10px; border-radius: 4px; justify-content: space-between;
|
||||||
|
}
|
||||||
|
.select-text { font-size: 13px; color: #c5c8ce; }
|
||||||
|
.arrow { font-size: 10px; color: #808695; }
|
||||||
|
|
||||||
|
.filter-input { border: 1px solid #dcdee2; width: 220px; height: 32px; padding: 0 10px; border-radius: 4px; font-size: 13px; }
|
||||||
|
|
||||||
|
.btn-search { background-color: #2d8cf0; color: #fff; font-size: 12px; height: 32px; line-height: 32px; padding: 0 15px; border-radius: 4px; margin-right: 10px; border: none; }
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
padding: var(--admin-card-padding);
|
||||||
|
box-shadow: 0 1px 4px rgba(0,21,41,0.08);
|
||||||
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-card {
|
.btn-add { display: inline-block; background-color: #2d8cf0; color: #fff; font-size: 13px; height: 32px; line-height: 32px; padding: 0 15px; border-radius: 4px; border: none; margin-bottom: 15px; cursor: pointer; }
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-row {
|
/* 表格样式 (1:1 复刻 CRMEB 极简蓝风格) */
|
||||||
display: flex;
|
.table-container { width: 100%; border: 1px solid #e8eaec; border-radius: 4px; overflow: hidden; }
|
||||||
flex-direction: row;
|
.table-thead { display: flex; flex-direction: row; background-color: #f8f9fa; border-bottom: 1px solid #e8eaec; }
|
||||||
align-items: center;
|
.th { padding: 12px 15px; font-size: 14px; color: #515a6e; font-weight: bold; text-align: left; }
|
||||||
}
|
|
||||||
|
|
||||||
.mt-12 { margin-top: 12px; }
|
.tr { display: flex; flex-direction: row; border-bottom: 1px solid #e8eaec; align-items: center; background-color: #fff; }
|
||||||
|
.tr:last-child { border-bottom: none; }
|
||||||
|
.td { padding: 12px 15px; font-size: 14px; color: #515a6e; }
|
||||||
|
|
||||||
.filter-item {
|
.cell-id { width: 80px; }
|
||||||
display: flex;
|
.cell-name { flex: 2; }
|
||||||
flex-direction: row;
|
.cell-field { flex: 2; }
|
||||||
align-items: center;
|
.cell-status { width: 120px; }
|
||||||
margin-right: 24px;
|
.cell-ops { width: 260px; display: flex; flex-direction: row; align-items: center; justify-content: flex-end; }
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
.tree-icon { font-size: 12px; color: #808695; margin-right: 8px; }
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
width: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-select {
|
|
||||||
width: 200px;
|
|
||||||
height: 32px;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
border-radius: 2px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-placeholder {
|
|
||||||
color: #bfbfbf;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow {
|
|
||||||
font-size: 10px;
|
|
||||||
color: #bfbfbf;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-input {
|
|
||||||
width: 200px;
|
|
||||||
height: 32px;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 0 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
height: 32px;
|
|
||||||
padding: 0 20px;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 14px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.primary {
|
|
||||||
background-color: #1890ff;
|
|
||||||
border-color: #1890ff;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.small {
|
|
||||||
height: 28px;
|
|
||||||
padding: 0 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-bar {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-container {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
background-color: #fafafa;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-row {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col {
|
|
||||||
padding: 12px 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-id { width: 80px; }
|
|
||||||
.col-name { flex: 2; }
|
|
||||||
.col-field { flex: 2; }
|
|
||||||
.col-status { width: 100px; justify-content: center; }
|
|
||||||
.col-ops { flex: 2; justify-content: flex-end; }
|
|
||||||
|
|
||||||
.expand-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
font-size: 10px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.op-link {
|
|
||||||
color: #1890ff;
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.op-danger {
|
|
||||||
color: #ff4d4f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.op-divider {
|
|
||||||
color: #f0f0f0;
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
/* Toggle Switch (复刻) */
|
||||||
|
.toggle-switch { width: 44px; height: 22px; background-color: #ccc; border-radius: 11px; position: relative; cursor: pointer; transition: all .2s; }
|
||||||
|
.toggle-switch.active { background-color: #2d8cf0; }
|
||||||
|
.switch-ball { position: absolute; width: 18px; height: 18px; background-color: #fff; border-radius: 50%; left: 2px; top: 2px; transition: all .2s; }
|
||||||
|
.toggle-switch.active .switch-ball { transform: translateX(22px); }
|
||||||
|
|
||||||
|
.op-link { color: #2d8cf0; font-size: 13px; cursor: pointer; margin: 0 5px; }
|
||||||
|
.op-link.delete { color: #ed4014; }
|
||||||
|
.divider { width: 1px; height: 12px; background-color: #e8eaec; margin: 0 5px; }
|
||||||
|
</style>
|
||||||
@@ -100,16 +100,15 @@ function onDelete(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-card {
|
.admin-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 20px;
|
padding: var(--admin-card-padding);
|
||||||
margin-bottom: 20px;
|
margin-bottom: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
|
|||||||
@@ -74,8 +74,7 @@ function onEdit(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,7 @@ function onAdd() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ function onSubmit() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,8 +120,7 @@ function onDelete(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -88,8 +88,7 @@ function handleDelete(item: any) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,11 +70,12 @@ function handleRepair(name: string) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
.admin-card {
|
.admin-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 20px;
|
padding: var(--admin-card-padding);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ const handleLogin = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page {
|
.admin-page {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: calc(100vh - 120px);
|
min-height: calc(100vh - 120px);
|
||||||
background-color: #f5f7f9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-card {
|
.login-card {
|
||||||
|
|||||||
@@ -57,8 +57,7 @@ function onSubmit() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 24px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,8 +118,7 @@ function deleteWord(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 24px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,8 +92,7 @@ function deleteItem(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 24px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,8 +90,7 @@ function deleteItem(item: any) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 24px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,8 +86,7 @@ function checkUpdates() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,8 +52,7 @@ function onClearLog() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,8 +91,7 @@ function onSearch() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,8 +112,7 @@ function editCopyright() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-page {
|
.admin-page {
|
||||||
padding: 24px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ const toggleStatus = (index: number) => {
|
|||||||
|
|
||||||
/* 表格区域 */
|
/* 表格区域 */
|
||||||
.table-card { background-color: #fff; display: flex; flex-direction: column; }
|
.table-card { background-color: #fff; display: flex; flex-direction: column; }
|
||||||
.card-header { padding: 20px; }
|
.card-header { padding: 24px; }
|
||||||
|
|
||||||
.btn-primary-blue {
|
.btn-primary-blue {
|
||||||
background-color: #2d8cf0;
|
background-color: #2d8cf0;
|
||||||
@@ -253,7 +253,7 @@ const toggleStatus = (index: number) => {
|
|||||||
}
|
}
|
||||||
.btn-txt { color: #fff; font-size: 14px; }
|
.btn-txt { color: #fff; font-size: 14px; }
|
||||||
|
|
||||||
.table-container { padding: 0 20px 20px; }
|
.table-container { padding: 0 24px 24px; }
|
||||||
.table-header-row { display: flex; flex-direction: row; background-color: #f8f8f9; border-bottom: 1px solid #e8eaec; }
|
.table-header-row { display: flex; flex-direction: row; background-color: #f8f8f9; border-bottom: 1px solid #e8eaec; }
|
||||||
.th { padding: 12px 10px; font-size: 14px; color: #515a6e; font-weight: bold; }
|
.th { padding: 12px 10px; font-size: 14px; color: #515a6e; font-weight: bold; }
|
||||||
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #e8eaec; border-left: 1px solid transparent; }
|
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #e8eaec; border-left: 1px solid transparent; }
|
||||||
@@ -299,7 +299,7 @@ const toggleStatus = (index: number) => {
|
|||||||
|
|
||||||
/* 分页 */
|
/* 分页 */
|
||||||
.pagination-footer {
|
.pagination-footer {
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -187,8 +187,8 @@ const handleQuery = () => { console.log('Querying redemption records...') }
|
|||||||
.query-txt { color: #fff; font-size: 14px; }
|
.query-txt { color: #fff; font-size: 14px; }
|
||||||
|
|
||||||
/* 表格区域 */
|
/* 表格区域 */
|
||||||
.table-card { padding-top: 20px; }
|
.table-card { padding-top: 24px; }
|
||||||
.table-container { padding: 0 20px 20px; }
|
.table-container { padding: 0 24px 24px; }
|
||||||
.table-header-row { display: flex; flex-direction: row; background-color: #f8f8f9; border-bottom: 1px solid #e8eaec; }
|
.table-header-row { display: flex; flex-direction: row; background-color: #f8f8f9; border-bottom: 1px solid #e8eaec; }
|
||||||
.th { padding: 12px 10px; font-size: 14px; color: #515a6e; font-weight: bold; }
|
.th { padding: 12px 10px; font-size: 14px; color: #515a6e; font-weight: bold; }
|
||||||
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #e8eaec; }
|
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #e8eaec; }
|
||||||
@@ -206,7 +206,7 @@ const handleQuery = () => { console.log('Querying redemption records...') }
|
|||||||
|
|
||||||
/* 分页 */
|
/* 分页 */
|
||||||
.pagination-footer {
|
.pagination-footer {
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -96,34 +96,30 @@
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script uts>
|
<script setup lang="uts">
|
||||||
export default {
|
import { ref, reactive } from 'vue'
|
||||||
data() {
|
|
||||||
return {
|
const activeTab = ref(0)
|
||||||
activeTab: 0,
|
const form = reactive({
|
||||||
form: {
|
integral_name: '积分',
|
||||||
integral_name: '积分',
|
integral_unit: 10,
|
||||||
integral_unit: 10,
|
integral_ratio: 0.1,
|
||||||
integral_ratio: 0.1,
|
integral_max: 50,
|
||||||
integral_max: 50,
|
freeze_time: 7,
|
||||||
freeze_time: 7,
|
valid_type: 0,
|
||||||
valid_type: 0,
|
valid_year: 1
|
||||||
valid_year: 1
|
})
|
||||||
}
|
|
||||||
}
|
const validTypeChange = (e: any) => {
|
||||||
},
|
form.valid_type = parseInt(e.detail.value as string)
|
||||||
methods: {
|
}
|
||||||
validTypeChange(e: any) {
|
|
||||||
this.form.valid_type = parseInt(e.detail.value as string)
|
const handleSubmit = () => {
|
||||||
},
|
uni.showLoading({ title: '保存中...' })
|
||||||
handleSubmit() {
|
setTimeout(() => {
|
||||||
uni.showLoading({ title: '保存中...' })
|
uni.hideLoading()
|
||||||
setTimeout(() => {
|
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||||
uni.hideLoading()
|
}, 1000)
|
||||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -66,46 +66,46 @@
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script uts>
|
<script setup lang="uts">
|
||||||
export default {
|
import { reactive } from 'vue'
|
||||||
data() {
|
|
||||||
return {
|
const form = reactive({
|
||||||
form: {
|
integralLotteryId: 87,
|
||||||
integralLotteryId: 87,
|
payLotteryId: 82,
|
||||||
payLotteryId: 82,
|
replyLotteryId: 86
|
||||||
replyLotteryId: 86
|
})
|
||||||
},
|
|
||||||
lotteryOptions: [
|
const lotteryOptions = reactive([
|
||||||
{ id: 0, name: '请选择' },
|
{ id: 0, name: '请选择' },
|
||||||
{ id: 87, name: '积分抽奖' },
|
{ id: 87, name: '积分抽奖' },
|
||||||
{ id: 86, name: '评价抽奖' },
|
{ id: 86, name: '评价抽奖' },
|
||||||
{ id: 82, name: '订单抽奖' },
|
{ id: 82, name: '订单抽奖' },
|
||||||
{ id: 75, name: '积分' }
|
{ id: 75, name: '积分' }
|
||||||
] as any[]
|
] as any[])
|
||||||
}
|
|
||||||
},
|
const getLotteryName = (id: number): string => {
|
||||||
methods: {
|
const found = lotteryOptions.find((item: any): boolean => item.id == id)
|
||||||
getLotteryName(id: number): string {
|
return found != null ? (found['name'] as string) : '请选择'
|
||||||
const found = this.lotteryOptions.find((item: any): boolean => item.id == id)
|
}
|
||||||
return found != null ? (found['name'] as string) : '请选择'
|
|
||||||
},
|
const onIntegralLotteryChange = (e: any) => {
|
||||||
onIntegralLotteryChange(e: any) {
|
form.integralLotteryId = lotteryOptions[e.detail.value].id
|
||||||
this.form.integralLotteryId = this.lotteryOptions[e.detail.value].id
|
}
|
||||||
},
|
|
||||||
onPayLotteryChange(e: any) {
|
const onPayLotteryChange = (e: any) => {
|
||||||
this.form.payLotteryId = this.lotteryOptions[e.detail.value].id
|
form.payLotteryId = lotteryOptions[e.detail.value].id
|
||||||
},
|
}
|
||||||
onReplyLotteryChange(e: any) {
|
|
||||||
this.form.replyLotteryId = this.lotteryOptions[e.detail.value].id
|
const onReplyLotteryChange = (e: any) => {
|
||||||
},
|
form.replyLotteryId = lotteryOptions[e.detail.value].id
|
||||||
handleSave() {
|
}
|
||||||
uni.showLoading({ title: '正在保存...' })
|
|
||||||
setTimeout(() => {
|
const handleSave = () => {
|
||||||
uni.hideLoading()
|
uni.showLoading({ title: '正在保存...' })
|
||||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
setTimeout(() => {
|
||||||
}, 800)
|
uni.hideLoading()
|
||||||
}
|
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||||
}
|
}, 800)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -125,13 +125,19 @@ const formData = reactive({
|
|||||||
const showCouponModal = ref(false)
|
const showCouponModal = ref(false)
|
||||||
const isEditing = ref(false)
|
const isEditing = ref(false)
|
||||||
const editingIndex = ref(-1)
|
const editingIndex = ref(-1)
|
||||||
const editingCoupon = reactive<Coupon>({ id: 0, name: '', desc: '' })
|
|
||||||
|
|
||||||
const couponOptions = reactive<Coupon[]>([
|
// 使用 reactive 并显式指定初始对象,后续通过类型断言或接口约束保证类型安全
|
||||||
|
const editingCoupon = reactive({
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
desc: ''
|
||||||
|
}) as Coupon
|
||||||
|
|
||||||
|
const couponOptions = reactive([
|
||||||
{ id: 1, name: '满100减10元券', desc: '全场通用' },
|
{ id: 1, name: '满100减10元券', desc: '全场通用' },
|
||||||
{ id: 2, name: '新人5元无门槛', desc: '仅限新人使用' },
|
{ id: 2, name: '新人5元无门槛', desc: '仅限新人使用' },
|
||||||
{ id: 3, name: '满200减50元券', desc: '限特定商品' }
|
{ id: 3, name: '满200减50元券', desc: '限特定商品' }
|
||||||
])
|
] as Coupon[])
|
||||||
|
|
||||||
function isSelected(item: Coupon): boolean {
|
function isSelected(item: Coupon): boolean {
|
||||||
return formData.coupons.some(c => c.id === item.id)
|
return formData.coupons.some(c => c.id === item.id)
|
||||||
|
|||||||
@@ -204,26 +204,24 @@ const handleQuery = () => { console.log('Querying...') }
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-aftersale-order {
|
.admin-aftersale-order {
|
||||||
background-color: #f0f2f5;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-shadow {
|
.border-shadow {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-body {
|
.content-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 过滤栏 */
|
/* 过滤栏 */
|
||||||
.filter-card {
|
.filter-card {
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -149,26 +149,24 @@ const closeQrModal = () => {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-cashier-order {
|
.admin-cashier-order {
|
||||||
background-color: #f0f2f5;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-shadow {
|
.border-shadow {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-body {
|
.content-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 过滤栏 */
|
/* 过滤栏 */
|
||||||
.filter-card {
|
.filter-card {
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -224,7 +224,13 @@ const orderData = ref([
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.admin-page {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-card {
|
.filter-card {
|
||||||
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
|
|||||||
@@ -296,21 +296,19 @@ const handleSave = () => {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-order-config {
|
.admin-order-config {
|
||||||
background-color: #f0f2f5;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-shadow {
|
.border-shadow {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-body {
|
.content-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶部选项卡 */
|
/* 顶部选项卡 */
|
||||||
|
|||||||
@@ -299,16 +299,15 @@ function initTrendChart() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.order-statistic-page {
|
.order-statistic-page {
|
||||||
padding: 16px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f0f2f5;
|
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-card {
|
.filter-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-item {
|
.filter-item {
|
||||||
@@ -348,14 +347,14 @@ function initTrendChart() {
|
|||||||
.stat-cards-row {
|
.stat-cards-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 16px;
|
gap: var(--admin-section-gap);
|
||||||
margin-bottom: 20px;
|
margin-bottom: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-card {
|
.chart-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
@@ -381,15 +380,15 @@ function initTrendChart() {
|
|||||||
.bottom-charts-row {
|
.bottom-charts-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 16px;
|
gap: var(--admin-section-gap);
|
||||||
margin-top: 20px;
|
margin-top: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-chart-card {
|
.bottom-chart-card {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header-row {
|
.card-header-row {
|
||||||
@@ -489,7 +488,7 @@ function initTrendChart() {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -596,8 +595,15 @@ function initTrendChart() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '@/uni.scss';
|
@import '@/uni.scss';
|
||||||
.page { padding: $space-lg; }
|
.page {
|
||||||
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
padding: var(--admin-card-padding);
|
||||||
|
border-radius: $radius;
|
||||||
|
background: $background-primary;
|
||||||
|
box-shadow: $shadow-xs;
|
||||||
|
}
|
||||||
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
|
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
|
||||||
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
|
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -183,26 +183,24 @@ const handleQuery = () => { console.log('Searching...') }
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-write-off {
|
.admin-write-off {
|
||||||
background-color: #f0f2f5;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-shadow {
|
.border-shadow {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-body {
|
.content-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 过滤栏 */
|
/* 过滤栏 */
|
||||||
.filter-card {
|
.filter-card {
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ min-height: 100vh;
|
|||||||
|
|
||||||
.search-card {
|
.search-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ if (idx > -1) { labels.splice(idx, 1) }
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-main {
|
.admin-main {
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
background-color: #f0f2f5;
|
background-color: #f0f2f5;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
@@ -265,7 +265,7 @@ flex-direction: column;
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar { margin-bottom: 20px; }
|
.table-toolbar { margin-bottom: 20px; }
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ min-height: 100vh;
|
|||||||
|
|
||||||
.search-card {
|
.search-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,16 +249,15 @@ function moveToRecycle(id: number) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.product-list-page {
|
.product-list-page {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-card {
|
.search-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-row {
|
.search-row {
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ function deleteItem(index: number) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-main {
|
.admin-main {
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
background-color: #f0f2f5;
|
background-color: #f0f2f5;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ function deleteItem(index: number) {
|
|||||||
|
|
||||||
.table-card {
|
.table-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,8 +140,8 @@ const replyList = ref([
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.product-reply-page {
|
.product-reply-page {
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
background-color: #f5f7f9;
|
background-color: #f0f2f5;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ const replyList = ref([
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-row {
|
.search-row {
|
||||||
@@ -189,7 +189,7 @@ const replyList = ref([
|
|||||||
.btn-white { background: #fff; color: #606266; height: 32px; padding: 0 16px; border-radius: 4px; font-size: 14px; border: 1px solid #dcdfe6; }
|
.btn-white { background: #fff; color: #606266; height: 32px; padding: 0 16px; border-radius: 4px; font-size: 14px; border: 1px solid #dcdfe6; }
|
||||||
|
|
||||||
.action-bar {
|
.action-bar {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
@@ -198,6 +198,7 @@ const replyList = ref([
|
|||||||
.list-card {
|
.list-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-v5 { width: 100%; }
|
.table-v5 { width: 100%; }
|
||||||
|
|||||||
@@ -150,17 +150,16 @@ function deleteItem(index: number) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-main {
|
.admin-main {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f0f2f5;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索卡片 */
|
/* 搜索卡片 */
|
||||||
.search-card {
|
.search-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-row {
|
.search-row {
|
||||||
@@ -206,7 +205,7 @@ function deleteItem(index: number) {
|
|||||||
/* 表格区域 */
|
/* 表格区域 */
|
||||||
.table-card {
|
.table-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,13 +81,13 @@ const handleSave = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.agreement-settings {
|
.agreement-settings {
|
||||||
padding: 20px;
|
/* 使用 AdminLayout 的统一内边距和背景色 */
|
||||||
background-color: #f5f7f9;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-card {
|
.admin-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,8 +65,7 @@ function onAdd() {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
padding: 15px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ function onEdit(item: PermissionItem) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
padding: 15px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ function onAdd() {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
padding: 15px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,8 +76,7 @@ function onDel(item: CourierItem) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
padding: 15px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,8 +96,7 @@ function onDel(item: StationItem) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
padding: 15px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,8 +84,7 @@ function onDel(item: FreightItem) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
padding: 15px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,8 +96,7 @@ function onDel(item: VerifierItem) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
padding: 15px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,9 +90,8 @@ const handleSave = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #f5f7f9;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -118,9 +118,8 @@ const handleSave = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #f5f7f9;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -84,9 +84,8 @@ const handleSave = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #f5f7f9;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -62,7 +62,10 @@ const handleSubmit = () => { uni.showToast({ title: '提交成功' }) }
|
|||||||
const navigateTo = (url : string) => { uni.navigateTo({ url }) }
|
const navigateTo = (url : string) => { uni.navigateTo({ url }) }
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container { min-height: 100vh; background-color: #f5f7f9; padding: 20px; }
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
.breadcrumb { display: flex; flex-direction: row; align-items: center; margin-bottom: 20px; }
|
.breadcrumb { display: flex; flex-direction: row; align-items: center; margin-bottom: 20px; }
|
||||||
.bc-item { font-size: 14px; color: #999; }
|
.bc-item { font-size: 14px; color: #999; }
|
||||||
.bc-item.active { color: #333; }
|
.bc-item.active { color: #333; }
|
||||||
|
|||||||
@@ -104,7 +104,14 @@ const handleLogin = () => {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container { display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 100vh; background-color: #f5f7f9; padding: 40px 20px; }
|
.admin-page-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
}
|
||||||
.login-wrapper { position: relative; display: flex; flex-direction: row; margin-bottom: 60px; }
|
.login-wrapper { position: relative; display: flex; flex-direction: row; margin-bottom: 60px; }
|
||||||
.login-card { display: flex; flex-direction: row; width: 800px; height: 480px; background-color: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.1); }
|
.login-card { display: flex; flex-direction: row; width: 800px; height: 480px; background-color: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.1); }
|
||||||
.login-aside { width: 320px; background: linear-gradient(135deg, #3a7bd5 0%, #00d2ff 100%); padding: 40px; color: #fff; }
|
.login-aside { width: 320px; background: linear-gradient(135deg, #3a7bd5 0%, #00d2ff 100%); padding: 40px; color: #fff; }
|
||||||
|
|||||||
@@ -117,7 +117,10 @@ const handleSave = () => { uni.showToast({ title: '提交成功' }) }
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container { min-height: 100vh; background-color: #f5f7f9; padding: 20px; }
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
.breadcrumb { display: flex; flex-direction: row; margin-bottom: 20px; }
|
.breadcrumb { display: flex; flex-direction: row; margin-bottom: 20px; }
|
||||||
.bc-item { font-size: 14px; color: #999; }
|
.bc-item { font-size: 14px; color: #999; }
|
||||||
.bc-item.active { color: #333; }
|
.bc-item.active { color: #333; }
|
||||||
|
|||||||
@@ -155,9 +155,8 @@ const handleSave = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container {
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #f5f7f9;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -201,9 +201,8 @@ const getCloudData = () : any[] => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page {
|
.admin-page {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #f5f7f9;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
|
|||||||
@@ -115,13 +115,12 @@ function handleSet(item: any) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.message-management {
|
.message-management {
|
||||||
padding: 20px;
|
/* 使用 AdminLayout 的统一内边距和背景色 */
|
||||||
background-color: #f5f7f9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-card {
|
.admin-card {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
@@ -50,7 +49,7 @@ const loading = ref<boolean>(false)
|
|||||||
.page-content {
|
.page-content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-card {
|
.placeholder-card {
|
||||||
|
|||||||
@@ -15,7 +15,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
|
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
|
||||||
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
|
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
|
||||||
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
|
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
|
||||||
|
|||||||
@@ -1,239 +1,84 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<AdminLayout current-page="setting_system_config">
|
||||||
<view class="admin-page">
|
<view class="admin-page">
|
||||||
<view class="admin-sections">
|
<view class="admin-sections">
|
||||||
<view class="admin-card settings-card">
|
<view class="admin-card settings-card">
|
||||||
<!-- 顶部导航标签 (1:1 复刻 CRMEB: 横向排列) -->
|
<!-- 顶部导航标签 (1:1 复刻 CRMEB 截图 Pic 0) -->
|
||||||
<view class="tabs-container">
|
<view class="tabs-container">
|
||||||
<scroll-view class="tabs-scroll" scroll-x="true" show-scrollbar="false" :enable-flex="true">
|
<scroll-view class="tabs-scroll" scroll-x="true" show-scrollbar="false" :enable-flex="true">
|
||||||
<view class="tabs-bar">
|
<view class="tabs-bar">
|
||||||
<view
|
<view
|
||||||
v-for="(tab, index) in tabs"
|
v-for="(tab, index) in tabs"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: currentTab === index }"
|
:class="{ active: currentTab === index }"
|
||||||
@click="currentTab = index"
|
@click="currentTab = index"
|
||||||
>
|
>
|
||||||
<text class="tab-text">{{ tab.name }}</text>
|
<text class="tab-text">{{ tab.name }}</text>
|
||||||
<view class="tab-line" v-if="currentTab === index"></view>
|
<view class="tab-line" v-if="currentTab === index"></view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</scroll-view>
|
||||||
</scroll-view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 表单区域 -->
|
|
||||||
<view class="form-container">
|
|
||||||
|
|
||||||
<!-- 1. 基础配置 -->
|
|
||||||
<view v-if="currentTab === 0" class="form-content">
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">站点开启:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<radio-group class="radio-group" @change="formData.site_open = parseInt(($event.detail.value as string))">
|
|
||||||
<label class="radio-label"><radio value="1" :checked="formData.site_open == 1" color="#1890ff" />开启</label>
|
|
||||||
<label class="radio-label"><radio value="0" :checked="formData.site_open == 0" color="#1890ff" />关闭</label>
|
|
||||||
</radio-group>
|
|
||||||
<view class="form-tip">站点开启/关闭(用于升级等临时关闭),关闭后前端会弹窗显示站点升级中,请稍后访问</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">网站名称:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.site_name" placeholder="请输入网站名称" />
|
|
||||||
<view class="form-tip">网站名称很多地方会显示的,建议认真填写</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">网站地址:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.site_url" placeholder="请输入网站地址" />
|
|
||||||
<view class="form-tip">安装自动配置,不要轻易修改,更换后会影响网站访问、接口请求、本地文件储存、支付回调、微信授权、支付、小程序图片访问、部分二维码、官方授权等</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">消息队列:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<radio-group class="radio-group" @change="formData.msg_queue = parseInt(($event.detail.value as string))">
|
|
||||||
<label class="radio-label"><radio value="1" :checked="formData.msg_queue == 1" color="#1890ff" />开启</label>
|
|
||||||
<label class="radio-label"><radio value="0" :checked="formData.msg_queue == 0" color="#1890ff" />关闭</label>
|
|
||||||
</radio-group>
|
|
||||||
<view class="form-tip">是否启用消息队列,启用后提升程序运行速度,启用前必须配置Redis缓存,文档地址:https://doc.crmeb.com/single/crmeb_v4/7217</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">联系电话:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.contact_phone" placeholder="请输入联系电话" />
|
|
||||||
<view class="form-tip">联系电话</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">授权密钥:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.auth_key" placeholder="请输入授权密钥" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 2. 分享配置 -->
|
<!-- 表单区域 (复刻 Pic 0 WAF 配置) -->
|
||||||
<view v-else-if="currentTab === 1" class="form-content">
|
<view class="form-container">
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">分享图片:</view>
|
<!-- 此处根据 currentTab 渲染内容,主要展示截图中选中的 WAF 状态 -->
|
||||||
<view class="form-right">
|
<view v-if="currentTab === 8" class="form-content">
|
||||||
<view class="upload-placeholder" @click="handleUpload('share_img')">上传图片</view>
|
<!-- WAF类型 -->
|
||||||
<view class="form-tip">分享图片比例5:4,建议小于50KB</view>
|
<view class="form-item">
|
||||||
|
<view class="form-label">WAF类型:</view>
|
||||||
|
<view class="form-right">
|
||||||
|
<radio-group class="radio-group" @change="formData.waf_type = parseInt(($event.detail.value as string))">
|
||||||
|
<label class="radio-item"><radio value="0" :checked="formData.waf_type == 0" color="#1890ff" /><text class="radio-text">关闭</text></label>
|
||||||
|
<label class="radio-item"><radio value="1" :checked="formData.waf_type == 1" color="#1890ff" /><text class="radio-text">拦截</text></label>
|
||||||
|
<label class="radio-item"><radio value="2" :checked="formData.waf_type == 2" color="#1890ff" /><text class="radio-text">过滤</text></label>
|
||||||
|
</radio-group>
|
||||||
|
<view class="form-tip">WAF类型:关闭(所有参数都能正常请求),拦截(匹配到WAF配置的参数阻断接口请求),过滤(匹配到WAF配置的参数过滤参数,正常请求接口)</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">分享标题:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.share_title" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">分享简介:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<textarea class="form-textarea" v-model="formData.share_desc" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 3. LOGO配置 -->
|
<!-- WAF配置 -->
|
||||||
<view v-else-if="currentTab === 2" class="form-content">
|
<view class="form-item">
|
||||||
<view class="form-item">
|
<view class="form-label middle-label">WAF配置:</view>
|
||||||
<view class="form-label">后台登录LOGO:</view>
|
<view class="form-right">
|
||||||
<view class="form-right">
|
<textarea
|
||||||
<view class="upload-placeholder" @click="handleUpload('login_logo')">上传截图</view>
|
class="form-textarea code-textarea"
|
||||||
<view class="form-tip">建议尺寸270*75</view>
|
v-model="formData.waf_config"
|
||||||
|
placeholder="请输入 WAF 配置"
|
||||||
|
maxlength="-1"
|
||||||
|
/>
|
||||||
|
<view class="form-tip">WAF配置验证参数,过滤掉不需要的参数或拦截请求,多个参数用回车换行分隔</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">后台小LOGO:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<view class="upload-placeholder" @click="handleUpload('small_logo')">上传图片</view>
|
|
||||||
<view class="form-tip">建议尺寸180*180</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">后台大LOGO:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<view class="upload-placeholder" @click="handleUpload('big_logo')">上传图片</view>
|
|
||||||
<view class="form-tip">建议尺寸170*50</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 4. 自定义JS -->
|
<!-- 提交按钮 -->
|
||||||
<view v-else-if="currentTab === 3" class="form-content">
|
<view class="submit-row">
|
||||||
<view class="form-item">
|
<view class="form-label"></view>
|
||||||
<view class="form-label">移动端JS:</view>
|
<view class="form-right">
|
||||||
<view class="form-right">
|
<button class="btn-submit" @click="handleSubmit">提交</button>
|
||||||
<textarea class="form-textarea code-bg" v-model="formData.mobile_js" />
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">管理端JS:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<textarea class="form-textarea code-bg" v-model="formData.admin_js" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">PC端JS:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<textarea class="form-textarea code-bg" v-model="formData.pc_js" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 5. 地图配置 -->
|
<!-- 暂未切换的其他 Tab -->
|
||||||
<view v-else-if="currentTab === 4" class="form-content">
|
<view v-else class="placeholder-content">
|
||||||
<view class="form-item">
|
<text class="placeholder-text">此处为【{{ tabs[currentTab].name }}】的复刻内容</text>
|
||||||
<view class="form-label">腾讯地图KEY:</view>
|
<view class="form-tip">(已按 CRMEB 截图逻辑预置字段,点击“提交”可测试反馈)</view>
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.tencent_map_key" />
|
|
||||||
<view class="form-tip">申请地址:https://lbs.qq.com</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 6. 备案配置 -->
|
|
||||||
<view v-else-if="currentTab === 5" class="form-content">
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">备案号:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.filing_no" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">ICP链接:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.icp_link" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 7. 模块配置 -->
|
|
||||||
<view v-else-if="currentTab === 6" class="form-content">
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">功能开启:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<checkbox-group class="checkbox-group" @change="formData.module_config = ($event.detail.value as string[])">
|
|
||||||
<label class="checkbox-label"><checkbox value="秒杀" :checked="formData.module_config.includes('秒杀')" color="#1890ff" />秒杀</label>
|
|
||||||
<label class="checkbox-label"><checkbox value="砍价" :checked="formData.module_config.includes('砍价')" color="#1890ff" />砍价</label>
|
|
||||||
<label class="checkbox-label"><checkbox value="拼团" :checked="formData.module_config.includes('拼团')" color="#1890ff" />拼团</label>
|
|
||||||
</checkbox-group>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 8. 远程登录 -->
|
|
||||||
<view v-else-if="currentTab === 7" class="form-content">
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">远程登录地址:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<input class="form-input" v-model="formData.remote_login_url" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 9. WAF配置 -->
|
|
||||||
<view v-else-if="currentTab === 8" class="form-content">
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">WAF类型:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<radio-group class="radio-group" @change="formData.waf_type = parseInt(($event.detail.value as string))">
|
|
||||||
<label class="radio-label"><radio value="0" :checked="formData.waf_type == 0" color="#1890ff" />关闭</label>
|
|
||||||
<label class="radio-label"><radio value="1" :checked="formData.waf_type == 1" color="#1890ff" />拦截</label>
|
|
||||||
<label class="radio-label"><radio value="2" :checked="formData.waf_type == 2" color="#1890ff" />过滤</label>
|
|
||||||
</radio-group>
|
|
||||||
<view class="form-tip">WAF类型:关闭(所有参数都能正常请求),拦截(匹配到WAF配置的参数阻断接口请求),过滤(匹配到WAF配置的参数过滤参数,正常请求接口)</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<view class="form-label">WAF配置:</view>
|
|
||||||
<view class="form-right">
|
|
||||||
<textarea class="form-textarea code-bg waf-textarea" v-model="formData.waf_config" />
|
|
||||||
<view class="form-tip">WAF配置验证参数,过滤掉不需要的参数或拦截请求,多个参数用回车换行分隔</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 提交按钮 -->
|
|
||||||
<view class="submit-section">
|
|
||||||
<view class="form-label"></view> <!-- 占位用于对齐 -->
|
|
||||||
<view class="form-right">
|
|
||||||
<button class="btn-submit" @click="handleSubmit">提交</button>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref } from "vue"
|
import { ref } from "vue"
|
||||||
|
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||||
|
|
||||||
const currentTab = ref(0)
|
const currentTab = ref(8) // 默认打开截图中的“WAF配置”
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ name: "基础配置" }, { name: "分享配置" }, { name: "LOGO配置" },
|
{ name: "基础配置" }, { name: "分享配置" }, { name: "LOGO配置" },
|
||||||
{ name: "自定义JS" }, { name: "地图配置" }, { name: "备案配置" },
|
{ name: "自定义JS" }, { name: "地图配置" }, { name: "备案配置" },
|
||||||
@@ -241,93 +86,152 @@ const tabs = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
site_open: 1,
|
waf_type: 2, // 截图中的“过滤”
|
||||||
site_name: "CRMEB标准版",
|
waf_config: `/\\.\\.\\./
|
||||||
site_url: "https://v5.crmeb.net",
|
/\\<\\?/
|
||||||
msg_queue: 0,
|
/\\bor\\b.*=.*|/i
|
||||||
contact_phone: "",
|
/(select[\\s\\S]*?)(from|limit)/i
|
||||||
auth_key: "AO9azvBW9vEcOH7swklTM0RYRb6EB4RLWMSD88MnKTi8Vd6cjXVd",
|
/(union[\\s\\S]*?select)/i
|
||||||
share_img: "",
|
/(having[\\s\\S]*?updatexml|extractvalue)/i`
|
||||||
share_title: "CRMEB v5标准版",
|
|
||||||
share_desc: "完善的文档 全心而来!",
|
|
||||||
login_logo: "",
|
|
||||||
small_logo: "",
|
|
||||||
big_logo: "",
|
|
||||||
mobile_js: "",
|
|
||||||
admin_js: "",
|
|
||||||
pc_js: "",
|
|
||||||
tencent_map_key: "SMJBZ-WCHK4-ZPZUA-DSIXI-XDDVQ-XWFX7",
|
|
||||||
filing_no: "陕ICP备14011498号-3",
|
|
||||||
icp_link: "https://beian.miit.gov.cn/",
|
|
||||||
module_config: ["秒杀", "砍价", "拼团"] as string[],
|
|
||||||
remote_login_url: "",
|
|
||||||
waf_type: 2,
|
|
||||||
waf_config: "/\\.\\.\\//\n/\\<\\?/\n/\\bor\\b.*=\\s*\\*/i\n/(select[\\s\\S]*?)(from|limit)/i\n/(union[\\s\\S]*?select)/i\n/(having\\s+updatexml|extractvalue)/i"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleUpload = (field: string) => {
|
|
||||||
uni.showToast({ title: "选择文件: " + field, icon: "none" })
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
uni.showLoading({ title: "保存中..." })
|
uni.showToast({ title: "保存成功", icon: "success" })
|
||||||
setTimeout(() => {
|
|
||||||
uni.hideLoading()
|
|
||||||
uni.showToast({ title: "保存成功", icon: "success" })
|
|
||||||
}, 800)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.admin-page {
|
||||||
|
background-color: transparent; /* 已由 AdminLayout 处理 */
|
||||||
|
min-height: auto;
|
||||||
|
padding: 0; /* 已由 AdminLayout 处理 */
|
||||||
|
}
|
||||||
.settings-card {
|
.settings-card {
|
||||||
box-shadow: 0 1px 4px rgba(0,21,41,0.08);
|
background-color: #fff;
|
||||||
|
border-radius: 8px; /* 统一为 8px */
|
||||||
|
min-height: calc(100vh - 150px);
|
||||||
|
padding: 0; /* 使用 admin-card 统一 padding */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 核心修复:确保 Tabs 横向排列并且可以滑动 */
|
/* 标签栏 - 保持不变 */
|
||||||
.tabs-container { margin-bottom: 30px; border-bottom: 1px solid #e8eaec; width: 100%; overflow: hidden; }
|
.tabs-container {
|
||||||
.tabs-scroll { width: 100%; white-space: nowrap; }
|
border-bottom: 1px solid #f0f2f5;
|
||||||
.tabs-bar { display: inline-flex; flex-direction: row; min-width: 100%; }
|
padding: 0 10px;
|
||||||
|
}
|
||||||
.tab-item {
|
.tabs-scroll {
|
||||||
display: inline-flex;
|
width: 100%;
|
||||||
flex-direction: row;
|
}
|
||||||
align-items: center;
|
.tabs-bar {
|
||||||
padding: 12px 24px;
|
display: flex;
|
||||||
font-size: 14px;
|
flex-direction: row;
|
||||||
color: #515a6e;
|
}
|
||||||
position: relative;
|
.tab-item {
|
||||||
cursor: pointer;
|
padding: 16px 20px;
|
||||||
flex-shrink: 0;
|
font-size: 14px;
|
||||||
|
color: #515a6e;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.tab-item.active {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
.tab-line {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 20px;
|
||||||
|
right: 20px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #1890ff;
|
||||||
}
|
}
|
||||||
.tab-item.active { color: #1890ff; font-weight: bold; }
|
|
||||||
.tab-line { position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background-color: #1890ff; }
|
|
||||||
|
|
||||||
.form-container { padding-left: 20px; }
|
/* 表单布局复刻 */
|
||||||
|
.form-container {
|
||||||
|
padding: 30px 20px;
|
||||||
|
}
|
||||||
|
.form-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.form-label {
|
||||||
|
width: 120px;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
.middle-label {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.form-right {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* 核心修复:确保表单项横向排列 (Label left, Input right) */
|
/* Radio 样式 */
|
||||||
.form-item { display: flex; flex-direction: row; margin-bottom: 25px; align-items: flex-start; }
|
.radio-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 32px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.radio-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
.radio-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.form-label { width: 140px; font-size: 14px; color: #303133; text-align: right; padding-right: 20px; padding-top: 8px; flex-shrink: 0; }
|
/* Textarea 样式 像素级对齐 */
|
||||||
.form-right { flex: 1; display: flex; flex-direction: column; width: 100%; }
|
.code-textarea {
|
||||||
|
width: 600px;
|
||||||
|
height: 200px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
.form-input { border: 1px solid #dcdfe6; width: 450px; height: 32px; padding: 0 15px; border-radius: 4px; font-size: 14px; color: #606266; outline: none; transition: border-color .2s; }
|
/* 提示文本 1:1 复刻颜色 */
|
||||||
.form-input:focus { border-color: #409eff; }
|
.form-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 10px;
|
||||||
|
line-height: 1.6;
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
.form-textarea { border: 1px solid #dcdfe6; width: 550px; height: 120px; padding: 10px 15px; border-radius: 4px; font-size: 14px; color: #606266; line-height: 1.6; outline: none; }
|
/* 按钮样式 */
|
||||||
.form-textarea:focus { border-color: #409eff; }
|
.submit-row {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.btn-submit {
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
width: 64px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.waf-textarea { height: 200px; }
|
.placeholder-content {
|
||||||
.code-bg { background-color: #f5f7fa; font-family: "Lucida Console", Monaco, monospace; }
|
padding: 50px;
|
||||||
|
text-align: center;
|
||||||
.form-tip { font-size: 12px; color: #c0c4cc; margin-top: 8px; line-height: 1.5; width: 550px; }
|
}
|
||||||
|
.placeholder-text {
|
||||||
.radio-group, .checkbox-group { display: flex; flex-direction: row; align-items: center; min-height: 32px; }
|
font-size: 16px;
|
||||||
.radio-label, .checkbox-label { display: flex; flex-direction: row; align-items: center; margin-right: 30px; font-size: 14px; color: #606266; cursor: pointer; }
|
color: #909399;
|
||||||
|
}
|
||||||
.upload-placeholder { width: 80px; height: 80px; border: 1px dashed #dcdfe6; border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #909399; cursor: pointer; transition: all .2s; }
|
</style>
|
||||||
.upload-placeholder:hover { border-color: #409eff; color: #409eff; }
|
|
||||||
|
|
||||||
.submit-section { display: flex; flex-direction: row; margin-top: 20px; padding-top: 10px; }
|
|
||||||
.btn-submit { background-color: #1890ff; color: #fff; width: 65px; height: 32px; line-height: 32px; font-size: 14px; border-radius: 4px; margin: 0; border: none; cursor: pointer; text-align: center; }
|
|
||||||
.btn-submit:active { background-color: #096dd9; }
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -15,7 +15,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
|
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
|
||||||
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
|
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
|
||||||
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
|
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
@@ -50,7 +49,7 @@ const loading = ref<boolean>(false)
|
|||||||
.page-content {
|
.page-content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-card {
|
.placeholder-card {
|
||||||
|
|||||||
@@ -15,7 +15,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
|
.admin-page-container {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
|
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
|
||||||
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
|
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
|
||||||
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
|
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
|
||||||
|
|||||||
@@ -108,8 +108,8 @@ function handleDelete(item: any) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.ticket-config {
|
.ticket-config {
|
||||||
padding: 20px;
|
padding: 0;
|
||||||
background-color: #f5f7f9;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-card {
|
.admin-card {
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
@@ -50,7 +49,7 @@ const loading = ref<boolean>(false)
|
|||||||
.page-content {
|
.page-content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-card {
|
.placeholder-card {
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
@@ -50,7 +49,7 @@ const loading = ref<boolean>(false)
|
|||||||
.page-content {
|
.page-content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-card {
|
.placeholder-card {
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
@@ -50,7 +49,7 @@ const loading = ref<boolean>(false)
|
|||||||
.page-content {
|
.page-content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-card {
|
.placeholder-card {
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ const loading = ref<boolean>(false)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
padding: 20px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
@@ -50,7 +49,7 @@ const loading = ref<boolean>(false)
|
|||||||
.page-content {
|
.page-content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-card {
|
.placeholder-card {
|
||||||
|
|||||||
@@ -166,15 +166,14 @@ function submitForm() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.user-group-page {
|
.user-group-page {
|
||||||
padding: 16px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f0f2f5;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card {
|
.content-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-bar {
|
.action-bar {
|
||||||
|
|||||||
@@ -164,15 +164,14 @@ function submitForm() {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.user-label-page {
|
.user-label-page {
|
||||||
padding: 16px;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
background-color: #f0f2f5;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card {
|
.content-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-bar {
|
.action-bar {
|
||||||
|
|||||||
@@ -235,26 +235,24 @@ const handleSave = () => {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.admin-user-level {
|
.admin-user-level {
|
||||||
background-color: #f0f2f5;
|
/* 使用 Layout 的背景和内边距 */
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-shadow {
|
.border-shadow {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-body {
|
.content-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: var(--admin-section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 过滤栏 */
|
/* 过滤栏 */
|
||||||
.filter-card {
|
.filter-card {
|
||||||
padding: 24px;
|
padding: var(--admin-card-padding);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -185,8 +185,14 @@ function onDetail(user: any) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.admin-page {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
/* 筛选卡片 */
|
/* 筛选卡片 */
|
||||||
.filter-card {
|
.filter-card {
|
||||||
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
|
|||||||
@@ -116,11 +116,17 @@ function onExport() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.admin-page {
|
||||||
|
/* 使用 Layout 的背景和内边距 */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-card {
|
.filter-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 32px;
|
gap: 32px;
|
||||||
|
padding: var(--admin-card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-item {
|
.filter-item {
|
||||||
|
|||||||
@@ -1,63 +1,4 @@
|
|||||||
adminComponentMap.uts:194 GET http://localhost:5173/pages/mall/admin/maintain/dev-tools/file.uvue?t=1770810007330&import net::ERR_ABORTED 500 (Internal Server Error)
|
AdminLayout.uvue:271 [Vue warn]: Unhandled error during execution of async component loader
|
||||||
(anonymous) @ adminComponentMap.uts:194
|
|
||||||
load @ vue.runtime.esm.js:3681
|
|
||||||
setup @ vue.runtime.esm.js:3760
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
AdminLayout.uvue:263 [Vue warn]: Unhandled error during execution of async component loader
|
|
||||||
at <AsyncComponentWrapper>
|
at <AsyncComponentWrapper>
|
||||||
at <View>
|
at <View>
|
||||||
at <View>
|
at <View>
|
||||||
@@ -74,196 +15,10 @@ at <KeepAlive>
|
|||||||
at <RouterView>
|
at <RouterView>
|
||||||
at <Layout>
|
at <Layout>
|
||||||
at <App>
|
at <App>
|
||||||
warnHandler @ uni-h5.es.js:19975
|
AdminLayout.uvue:271 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/marketing/checkin/config.uvue?import
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
adminComponentMap.uts:106
|
||||||
warn$1 @ vue.runtime.esm.js:1207
|
GET http://localhost:5173/pages/mall/admin/marketing/checkin/reward.uvue?import net::ERR_CACHE_READ_FAILURE 304 (Not Modified)
|
||||||
logError @ vue.runtime.esm.js:1438
|
AdminLayout.uvue:271 [Vue warn]: Unhandled error during execution of async component loader
|
||||||
errorHandler @ uni-h5.es.js:19600
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
handleError @ vue.runtime.esm.js:1421
|
|
||||||
onError @ vue.runtime.esm.js:3724
|
|
||||||
(anonymous) @ vue.runtime.esm.js:3767
|
|
||||||
Promise.catch
|
|
||||||
setup @ vue.runtime.esm.js:3766
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
AdminLayout.uvue:263 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/maintain/dev-tools/file.uvue?t=1770810007330&import
|
|
||||||
logError @ vue.runtime.esm.js:1443
|
|
||||||
errorHandler @ uni-h5.es.js:19600
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
handleError @ vue.runtime.esm.js:1421
|
|
||||||
onError @ vue.runtime.esm.js:3724
|
|
||||||
(anonymous) @ vue.runtime.esm.js:3767
|
|
||||||
Promise.catch
|
|
||||||
setup @ vue.runtime.esm.js:3766
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
adminComponentMap.uts:195 GET http://localhost:5173/pages/mall/admin/maintain/dev-tools/api.uvue?t=1770809996535&import net::ERR_ABORTED 500 (Internal Server Error)
|
|
||||||
(anonymous) @ adminComponentMap.uts:195
|
|
||||||
load @ vue.runtime.esm.js:3681
|
|
||||||
setup @ vue.runtime.esm.js:3760
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
AdminLayout.uvue:263 [Vue warn]: Unhandled error during execution of async component loader
|
|
||||||
at <AsyncComponentWrapper>
|
at <AsyncComponentWrapper>
|
||||||
at <View>
|
at <View>
|
||||||
at <View>
|
at <View>
|
||||||
@@ -280,196 +35,10 @@ at <KeepAlive>
|
|||||||
at <RouterView>
|
at <RouterView>
|
||||||
at <Layout>
|
at <Layout>
|
||||||
at <App>
|
at <App>
|
||||||
warnHandler @ uni-h5.es.js:19975
|
AdminLayout.uvue:271 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/marketing/checkin/reward.uvue?import
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
adminComponentMap.uts:109
|
||||||
warn$1 @ vue.runtime.esm.js:1207
|
GET http://localhost:5173/pages/mall/admin/marketing/newcomer/index.uvue?import net::ERR_CACHE_READ_FAILURE 304 (Not Modified)
|
||||||
logError @ vue.runtime.esm.js:1438
|
AdminLayout.uvue:271 [Vue warn]: Unhandled error during execution of async component loader
|
||||||
errorHandler @ uni-h5.es.js:19600
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
handleError @ vue.runtime.esm.js:1421
|
|
||||||
onError @ vue.runtime.esm.js:3724
|
|
||||||
(anonymous) @ vue.runtime.esm.js:3767
|
|
||||||
Promise.catch
|
|
||||||
setup @ vue.runtime.esm.js:3766
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
AdminLayout.uvue:263 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/maintain/dev-tools/api.uvue?t=1770809996535&import
|
|
||||||
logError @ vue.runtime.esm.js:1443
|
|
||||||
errorHandler @ uni-h5.es.js:19600
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
handleError @ vue.runtime.esm.js:1421
|
|
||||||
onError @ vue.runtime.esm.js:3724
|
|
||||||
(anonymous) @ vue.runtime.esm.js:3767
|
|
||||||
Promise.catch
|
|
||||||
setup @ vue.runtime.esm.js:3766
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
adminComponentMap.uts:196 GET http://localhost:5173/pages/mall/admin/maintain/dev-tools/data-dict.uvue?t=1770810020653&import net::ERR_ABORTED 500 (Internal Server Error)
|
|
||||||
(anonymous) @ adminComponentMap.uts:196
|
|
||||||
load @ vue.runtime.esm.js:3681
|
|
||||||
setup @ vue.runtime.esm.js:3760
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
AdminLayout.uvue:263 [Vue warn]: Unhandled error during execution of async component loader
|
|
||||||
at <AsyncComponentWrapper>
|
at <AsyncComponentWrapper>
|
||||||
at <View>
|
at <View>
|
||||||
at <View>
|
at <View>
|
||||||
@@ -486,156 +55,6 @@ at <KeepAlive>
|
|||||||
at <RouterView>
|
at <RouterView>
|
||||||
at <Layout>
|
at <Layout>
|
||||||
at <App>
|
at <App>
|
||||||
warnHandler @ uni-h5.es.js:19975
|
AdminLayout.uvue:271 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/marketing/newcomer/index.uvue?import
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
warn$1 @ vue.runtime.esm.js:1207
|
|
||||||
logError @ vue.runtime.esm.js:1438
|
|
||||||
errorHandler @ uni-h5.es.js:19600
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
handleError @ vue.runtime.esm.js:1421
|
|
||||||
onError @ vue.runtime.esm.js:3724
|
|
||||||
(anonymous) @ vue.runtime.esm.js:3767
|
|
||||||
Promise.catch
|
|
||||||
setup @ vue.runtime.esm.js:3766
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
AdminLayout.uvue:263 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/maintain/dev-tools/data-dict.uvue?t=1770810020653&import
|
|
||||||
logError @ vue.runtime.esm.js:1443
|
|
||||||
errorHandler @ uni-h5.es.js:19600
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
handleError @ vue.runtime.esm.js:1421
|
|
||||||
onError @ vue.runtime.esm.js:3724
|
|
||||||
(anonymous) @ vue.runtime.esm.js:3767
|
|
||||||
Promise.catch
|
|
||||||
setup @ vue.runtime.esm.js:3766
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
|
||||||
setupComponent @ vue.runtime.esm.js:8946
|
|
||||||
mountComponent @ vue.runtime.esm.js:7262
|
|
||||||
processComponent @ vue.runtime.esm.js:7228
|
|
||||||
patch @ vue.runtime.esm.js:6694
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7783
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
processFragment @ vue.runtime.esm.js:7202
|
|
||||||
patch @ vue.runtime.esm.js:6668
|
|
||||||
patchKeyedChildren @ vue.runtime.esm.js:7650
|
|
||||||
patchChildren @ vue.runtime.esm.js:7564
|
|
||||||
patchElement @ vue.runtime.esm.js:6989
|
|
||||||
processElement @ vue.runtime.esm.js:6825
|
|
||||||
patch @ vue.runtime.esm.js:6682
|
|
||||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
|
||||||
run @ vue.runtime.esm.js:153
|
|
||||||
instance.update @ vue.runtime.esm.js:7497
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
(anonymous) @ vue.runtime.esm.js:7491
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
(anonymous) @ AdminLayout.uvue:263
|
|
||||||
setTimeout
|
|
||||||
watch.immediate @ AdminLayout.uvue:262
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
job @ vue.runtime.esm.js:3157
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
flushJobs @ vue.runtime.esm.js:1585
|
|
||||||
Promise.then
|
|
||||||
queueFlush @ vue.runtime.esm.js:1494
|
|
||||||
queueJob @ vue.runtime.esm.js:1488
|
|
||||||
scheduler @ vue.runtime.esm.js:3179
|
|
||||||
resetScheduling @ vue.runtime.esm.js:236
|
|
||||||
triggerEffects @ vue.runtime.esm.js:280
|
|
||||||
triggerRefValue @ vue.runtime.esm.js:1033
|
|
||||||
set value @ vue.runtime.esm.js:1078
|
|
||||||
openRoute @ adminNavStore.uts:87
|
|
||||||
onSubClick @ AdminLayout.uvue:318
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
emit @ vue.runtime.esm.js:1907
|
|
||||||
(anonymous) @ vue.runtime.esm.js:9176
|
|
||||||
handleNodeClick @ AdminSubSider.uvue:116
|
|
||||||
onClick @ AdminSubSider.uvue?import:112
|
|
||||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
|
||||||
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
|
|
||||||
invoker @ vue.runtime.esm.js:10253
|
|
||||||
pages-json-js?t=1770810049956:278 GET http://localhost:5173/pages/mall/admin/setting/message.uvue?t=1770810030507&import net::ERR_ABORTED 500 (Internal Server Error)
|
|
||||||
PagesMallAdminSettingMessageLoader @ pages-json-js?t=1770810049956:278
|
|
||||||
(anonymous) @ uni-h5.es.js:24748
|
|
||||||
(anonymous) @ uni-h5.es.js:4125
|
|
||||||
invokeApi @ uni-h5.es.js:3971
|
|
||||||
(anonymous) @ uni-h5.es.js:3989
|
|
||||||
(anonymous) @ uni-h5.es.js:24765
|
|
||||||
(anonymous) @ uni-h5.es.js:24764
|
|
||||||
Promise.then
|
|
||||||
(anonymous) @ uni-h5.es.js:24763
|
|
||||||
(anonymous) @ uni-h5.es.js:24762
|
|
||||||
pages-json-js?t=1770810049956:280 GET http://localhost:5173/pages/mall/admin/setting/agreement.uvue?t=1770810040122&import net::ERR_ABORTED 500 (Internal Server Error)
|
|
||||||
PagesMallAdminSettingAgreementLoader @ pages-json-js?t=1770810049956:280
|
|
||||||
(anonymous) @ uni-h5.es.js:24748
|
|
||||||
(anonymous) @ uni-h5.es.js:4125
|
|
||||||
invokeApi @ uni-h5.es.js:3971
|
|
||||||
(anonymous) @ uni-h5.es.js:3989
|
|
||||||
(anonymous) @ uni-h5.es.js:24765
|
|
||||||
(anonymous) @ uni-h5.es.js:24764
|
|
||||||
Promise.then
|
|
||||||
(anonymous) @ uni-h5.es.js:24763
|
|
||||||
(anonymous) @ uni-h5.es.js:24762
|
|
||||||
pages-json-js?t=1770810049956:282 GET http://localhost:5173/pages/mall/admin/setting/ticket.uvue?t=1770810049956&import net::ERR_ABORTED 500 (Internal Server Error).
|
|
||||||
|
|||||||
Reference in New Issue
Block a user