<script setup lang="ts">
|
import { nextTick, ref, watch } from 'vue'
|
import { useMessage } from 'naive-ui'
|
import {
|
getAgreementContent,
|
getVehicleDescription,
|
saveAgreement,
|
saveVehicleDescription,
|
} from '@/service/api/contentCenter'
|
import VehiclePrepareForm from './components/VehiclePrepareForm.vue'
|
import SimpleRichTextForm from './components/SimpleRichTextForm.vue'
|
import AgreementListForm from './components/AgreementListForm.vue'
|
|
const activeTab = ref('prepare')
|
const message = useMessage()
|
const loading = ref(false)
|
|
const tabs = [
|
{ key: 'prepare', label: '车辆整备' },
|
{ key: 'charge', label: '车辆充电' },
|
{ key: 'inspect', label: '车辆检测' },
|
{ key: 'insurance', label: '保险说明' },
|
{ key: 'annual', label: '年检说明' },
|
{ key: 'maintenance', label: '车辆保养' },
|
{ key: 'qualification', label: '运营资质' },
|
{ key: 'opsAppNotice', label: '登录协议' },
|
]
|
|
const contentFormRefs = ref<Record<string, any>>({})
|
|
async function handleSave() {
|
try {
|
const currentForm = contentFormRefs.value[activeTab.value]
|
if (currentForm) {
|
await currentForm.validate()
|
const data = currentForm.getData()
|
let saveRes: any = null
|
|
if (activeTab.value === 'prepare') {
|
const map: Record<string, number> = {
|
tirePressure: 1,
|
lighting: 2,
|
bodyClean: 3,
|
cabinClean: 4,
|
lostItems: 5,
|
}
|
const activeKey = typeof currentForm.getActiveKey === 'function' ? currentForm.getActiveKey() : 'tirePressure'
|
const t = map[activeKey] ?? 1
|
const content = data[activeKey] || ''
|
saveRes = await saveVehicleDescription({ descriptionType: t, descriptionContent: content })
|
}
|
else if (['charge', 'inspect', 'insurance', 'annual', 'maintenance', 'qualification'].includes(activeTab.value)) {
|
const typeMap: Record<string, number> = {
|
charge: 6,
|
inspect: 7,
|
insurance: 8,
|
annual: 9,
|
maintenance: 10,
|
qualification: 11,
|
}
|
const t = typeMap[activeTab.value]
|
saveRes = await saveVehicleDescription({ descriptionType: t, descriptionContent: data.content || '' })
|
}
|
else if (activeTab.value === 'opsAppNotice') {
|
const map: Record<string, number> = {
|
operationPrivacyPolicy: 8,
|
thirdPartyShareList: 9,
|
personalInfoCollectList: 10,
|
permissionCall: 11,
|
}
|
const activeKey = typeof currentForm.getActiveKey === 'function' ? currentForm.getActiveKey() : 'operationPrivacyPolicy'
|
const t = map[activeKey] ?? 8
|
const content = data[activeKey] || ''
|
saveRes = await saveAgreement({ content, contentType: t })
|
}
|
|
if (saveRes && saveRes.code === 200) {
|
message.success('保存成功')
|
}
|
else if (saveRes) {
|
message.error('保存失败')
|
}
|
}
|
}
|
catch {
|
// message.error('验证失败')
|
}
|
}
|
|
function handleCancel() {
|
|
}
|
|
async function loadContent(tabKey: string) {
|
try {
|
loading.value = true
|
await nextTick()
|
let currentForm = contentFormRefs.value[tabKey]
|
if (!currentForm || typeof currentForm.setData !== 'function') {
|
await nextTick()
|
currentForm = contentFormRefs.value[tabKey]
|
}
|
if (!currentForm || typeof currentForm.setData !== 'function') {
|
loading.value = false
|
return
|
}
|
|
if (tabKey === 'prepare') {
|
const types = [1, 2, 3, 4, 5]
|
const res = await Promise.all(types.map(t => getVehicleDescription(t)))
|
if (res.every(r => r && r.code === 200)) {
|
const data: Record<string, string> = {
|
tirePressure: res[0].data?.descriptionContent || '',
|
lighting: res[1].data?.descriptionContent || '',
|
bodyClean: res[2].data?.descriptionContent || '',
|
cabinClean: res[3].data?.descriptionContent || '',
|
lostItems: res[4].data?.descriptionContent || '',
|
}
|
currentForm.setData(data)
|
}
|
}
|
else if (['charge', 'inspect', 'insurance', 'annual', 'maintenance', 'qualification'].includes(tabKey)) {
|
const typeMap: Record<string, number> = {
|
charge: 6,
|
inspect: 7,
|
insurance: 8,
|
annual: 9,
|
maintenance: 10,
|
qualification: 11,
|
}
|
const t = typeMap[tabKey]
|
const res = await getVehicleDescription(t)
|
if (res && res.code === 200) {
|
currentForm.setData({ content: res.data?.descriptionContent || '' })
|
}
|
}
|
else if (tabKey === 'opsAppNotice') {
|
const types = [8, 9, 10, 11]
|
const res = await Promise.all(types.map(t => getAgreementContent(t)))
|
if (res.every(r => r && r.code === 200)) {
|
const data = {
|
operationPrivacyPolicy: res[0].data?.content || '',
|
thirdPartyShareList: res[1].data?.content || '',
|
personalInfoCollectList: res[2].data?.content || '',
|
permissionCall: res[3].data?.content || '',
|
}
|
currentForm.setData(data)
|
}
|
}
|
}
|
finally {
|
loading.value = false
|
}
|
}
|
|
watch(activeTab, (v) => {
|
loadContent(v)
|
}, { immediate: true })
|
</script>
|
|
<template>
|
<div class="h-full flex flex-col justify-between ">
|
<div class="p-12px content-page">
|
<n-card>
|
<div>
|
<div class="head">
|
运维APP内容管理
|
</div>
|
<n-tabs
|
v-model:value="activeTab"
|
type="line"
|
size="large"
|
:theme-overrides="{
|
tabTextColor: '#666666',
|
tabTextColorActive: '#0068FF',
|
tabBorderColorActive: '#0068FF',
|
tabBorderRadius: '4px',
|
tabPaddingHorizontal: '16px',
|
tabPaddingVertical: '8px',
|
}"
|
class="tab-container"
|
>
|
<n-tab-pane v-for="tab in tabs" :key="tab.key" :name="tab.key" :tab="tab.label">
|
<div style="height: calc(100vh - 304px); overflow-y: auto; padding-bottom: 40px;">
|
<VehiclePrepareForm
|
v-if="tab.key === 'prepare'"
|
:ref="(el) => { if (el) contentFormRefs[tab.key] = el }"
|
/>
|
<SimpleRichTextForm
|
v-else-if="['charge', 'inspect', 'insurance', 'annual', 'maintenance', 'qualification'].includes(tab.key)"
|
:ref="(el) => { if (el) contentFormRefs[tab.key] = el }"
|
/>
|
<AgreementListForm
|
v-else-if="tab.key === 'opsAppNotice'"
|
type="ops"
|
:ref="(el) => { if (el) contentFormRefs[tab.key] = el }"
|
/>
|
</div>
|
</n-tab-pane>
|
</n-tabs>
|
</div>
|
</n-card>
|
</div>
|
|
<div v-permission="30211" class="footer flex justify-end " style="background-color: white; padding: 16px 24px; border-radius: 8px; box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);">
|
<n-button style="padding: 8px 24px; border-radius: 4px; font-size: 14px;margin-right: 12px;" @click="handleCancel">
|
取消
|
</n-button>
|
<n-button type="primary" style="padding: 8px 24px; border-radius: 4px; font-size: 14px;" @click="handleSave">
|
保存
|
</n-button>
|
</div>
|
</div>
|
</template>
|
|
<style scoped lang="scss">
|
.content-page{
|
height: calc(100vh - 302px);
|
}
|
.tab-container {
|
// border-bottom: 1px solid #e8e8e8;
|
}
|
:deep(.n-tabs-wrapper){
|
padding: 0 10px;
|
}
|
|
:deep(.n-tabs-nav--line .n-tabs-tab) {
|
font-size: 14px;
|
font-weight: 500;
|
transition: all 0.3s ease;
|
}
|
|
:deep(.n-tabs-nav--line .n-tabs-tab:hover) {
|
color: #0068FF;
|
}
|
|
:deep(.n-tabs-nav--line .n-tabs-tab--active) {
|
font-weight: 600;
|
}
|
|
:deep(.n-tabs-nav--line .n-tabs-tab--active::after) {
|
height: 2px;
|
background-color: #0068FF;
|
border-radius: 2px;
|
}
|
|
/* 修复富文本编辑器超链接弹窗被遮挡的问题 */
|
.ql-tooltip {
|
z-index: 2000 !important;
|
left:-10px !important;
|
}
|
|
/* 允许内容溢出,防止 tooltip 被裁剪 */
|
.n-tabs .n-tab-pane,
|
.n-card > .n-card__content {
|
overflow: visible !important;
|
}
|
.n-tabs-tab__label{
|
font-size: 14px;
|
}
|
.head {
|
font-family: PingFang SC;
|
font-size: 16px;
|
font-weight: 600;
|
line-height: 24px;
|
letter-spacing: normal;
|
color: #171F26;
|
position: relative;
|
text-indent: 11px;
|
margin-bottom: 16px;
|
|
&::before {
|
content: '';
|
position: absolute;
|
top: 50%;
|
left: 0;
|
transform: translateY(-50%);
|
width: 3px;
|
height: 14px;
|
z-index: 0;
|
border-radius: 3px;
|
background: #0068FF;
|
}
|
|
}
|
.footer {
|
position: absolute;
|
bottom: 0;
|
left: 0;
|
width: 100%;
|
height: 64px;
|
background-color: #fff;
|
box-shadow: 0px 0px 5px 0px rgba(23, 31, 38, 0.05);
|
padding: 16px 24px;
|
|
}
|
</style>
|