提交 54da6b43 authored 作者: liuluyu's avatar liuluyu

更新计划管理页面样式、参数和流程

上级 fd7edf9b
import { defineStore } from 'pinia';
import { reactive, ref } from 'vue';
export const usePlanFormStore = defineStore('planFormStore', () => {
// 表单提交回调函数
const submitCallback = ref<(() => Promise<void>) | null>(null);
// 表单数据缓存
const formDataCache = reactive({
id: '',
executeStatus: '',
actualStartTime: null,
actualEndTime: null,
executeEcord: '',
attachments: [] as any[],
});
// 表单加载状态
const formLoadingState = reactive({
isLoading: false,
isError: false,
errorMessage: '',
lastLoadedId: '',
});
// 注册表单提交回调
const registerSubmitCallback = (callback: () => Promise<void>) => {
submitCallback.value = callback;
};
// 执行表单提交
const executeSubmit = async () => {
if (submitCallback.value) {
try {
formLoadingState.isError = false;
formLoadingState.errorMessage = '';
await submitCallback.value();
} catch (error: any) {
formLoadingState.isError = true;
formLoadingState.errorMessage = error?.message || '表单提交失败';
throw error;
}
}
};
// 更新表单数据缓存
const updateFormDataCache = (data: any) => {
Object.assign(formDataCache, data);
};
// 清除表单缓存
const clearFormCache = () => {
Object.assign(formDataCache, {
id: '',
executeStatus: '',
actualStartTime: null,
actualEndTime: null,
executeEcord: '',
attachments: [],
});
formLoadingState.lastLoadedId = '';
};
// 设置加载状态
const setLoadingState = (isLoading: boolean) => {
formLoadingState.isLoading = isLoading;
};
// 设置最后加载的ID
const setLastLoadedId = (id: string) => {
formLoadingState.lastLoadedId = id;
};
return {
submitCallback,
formDataCache,
formLoadingState,
registerSubmitCallback,
executeSubmit,
updateFormDataCache,
clearFormCache,
setLoadingState,
setLastLoadedId,
};
});
...@@ -34,11 +34,7 @@ ...@@ -34,11 +34,7 @@
@change="handleChange" @change="handleChange"
v-if="shownextNodeNameSele" v-if="shownextNodeNameSele"
> >
<a-select-option <a-select-option v-for="item in tableOptions" :key="item.id" :value="item.value">
v-for="item in tableOptions"
:key="item.id"
:value="item.value"
>
{{ item.name }} {{ item.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
...@@ -62,8 +58,8 @@ ...@@ -62,8 +58,8 @@
<div v-show="formTp == 1" class="form-wrapper"> <div v-show="formTp == 1" class="form-wrapper">
<FlowInnerForm ref="refCruInnerForm" :key="formKey" style="width: 100%" /> <FlowInnerForm ref="refCruInnerForm" :key="formKey" style="width: 100%" />
</div> </div>
<div v-show="formTp == 2" class="iframe-container" style="background-color: red; height: 470px"> <div v-show="formTp == 2" class="iframe-container" style="height: 470px">
<iFrame :src="formUrl" class="responsive-iframe" style="width: 100%; height: 100%" /> <iFrame :key="formKey" :src="formUrl" class="responsive-iframe" style="width: 100%; height: 100%" />
</div> </div>
</div> </div>
</a-card> </a-card>
...@@ -216,7 +212,7 @@ ...@@ -216,7 +212,7 @@
// API // API
import { flowRecord } from '/@/components/Process/api/finished'; import { flowRecord } from '/@/components/Process/api/finished';
import { flowXmlAndNode } from '/@/components/Process/api/definition'; import { flowXmlAndNode } from '/@/components/Process/api/definition';
import { complete, flowTaskForm, getNextFlowNode,getMyTaskFlow} from '/@/components/Process/api/todo'; import { complete, flowTaskForm, getNextFlowNode, getMyTaskFlow } from '/@/components/Process/api/todo';
import { flowTaskInfo } from '/@/components/Process/api/process'; import { flowTaskInfo } from '/@/components/Process/api/process';
// 组件 // 组件
...@@ -241,7 +237,6 @@ ...@@ -241,7 +237,6 @@
const formKey = ref(0); const formKey = ref(0);
const showApprovalUi = ref(true); const showApprovalUi = ref(true);
const flowData = ref<any>({}); const flowData = ref<any>({});
const flowRecordList = ref<any[]>([]); const flowRecordList = ref<any[]>([]);
const nextNodeName = ref(''); const nextNodeName = ref('');
...@@ -333,6 +328,7 @@ ...@@ -333,6 +328,7 @@
placeholder: '请选择接收人', placeholder: '请选择接收人',
mode: 'multiple', mode: 'multiple',
}, },
rules: [{ required: true, message: '请选择接收人' }],
}, },
{ {
label: '接收角色', label: '接收角色',
...@@ -387,13 +383,8 @@ ...@@ -387,13 +383,8 @@
await nextTick(); await nextTick();
if (!refCruInnerForm.value) {
throw new Error('表单组件未找到');
}
let { taskId } = taskForm; let { taskId } = taskForm;
if (!taskId) { if (!taskId) {
// try to resolve taskId by business id from workFlowData // try to resolve taskId by business id from workFlowData
const dataId = workFlowData.value?.dataId || workFlowData.value?.id || workFlowData.value?.businessId; const dataId = workFlowData.value?.dataId || workFlowData.value?.id || workFlowData.value?.businessId;
...@@ -416,17 +407,32 @@ ...@@ -416,17 +407,32 @@
const resData = await flowTaskForm({ taskId }); const resData = await flowTaskForm({ taskId });
const { flowForm = {} } = resData; const { flowForm = {} } = resData;
formTp.value = flowForm.formTp; // normalize form type to number for consistent comparisons
const tp = Number(flowForm.formTp);
formTp.value = tp;
if (flowForm.formTp == '1') { if (tp === 1) {
// ensure the inner form component is recreated before initializing
formKey.value++;
await nextTick();
if (!refCruInnerForm.value) {
// wait a tick more in case component hasn't mounted
await nextTick();
}
if (refCruInnerForm.value && typeof refCruInnerForm.value.iniData === 'function') {
await refCruInnerForm.value.iniData(flowForm); await refCruInnerForm.value.iniData(flowForm);
} else { } else {
let formValues = resData.formValues; throw new Error('内嵌表单组件未就绪');
}
} else {
let formValues = resData.formValues || {};
let formUrlparval = formValues.dataId; let formUrlparval = formValues.dataId;
let formUrlpar = formValues.dataName; let formUrlpar = formValues.dataName || 'id';
const connector = flowForm.formUrl.includes('?') ? '&' : '?'; const connector = flowForm.formUrl && flowForm.formUrl.includes('?') ? '&' : '?';
formUrl.value = flowForm.formUrl + connector + formUrlpar + '=' + formUrlparval; formUrl.value = (flowForm.formUrl || '') + connector + formUrlpar + '=' + formUrlparval;
//formUrl.value = flowForm.formUrl + '?' + formUrlpar + '=' + formUrlparval; // force iframe reload when src is set/changed
formKey.value++;
await nextTick();
console.error('表单url:', formUrl.value); console.error('表单url:', formUrl.value);
} }
formError.value = false; formError.value = false;
...@@ -517,7 +523,7 @@ ...@@ -517,7 +523,7 @@
values: {}, values: {},
}; };
//找到选择的下一步节点 //找到选择的下一步节点
if(nextNodenum.value>1){ if (nextNodenum.value > 1) {
submitData.values['userTaskid'] = nextNodeNameSelevue.value; submitData.values['userTaskid'] = nextNodeNameSelevue.value;
} }
...@@ -527,12 +533,11 @@ ...@@ -527,12 +533,11 @@
return false; return false;
} }
const formData = await validate(); const formData = await validate();
Object.assign(submitData, formData); Object.assign(submitData, formData);
submitData.comment = submitData.comment || ''; submitData.comment = submitData.comment || '';
//alert("dd"+isFixed.value); //alert("dd"+isFixed.value);
// if (!isFixed.value) { // if (!isFixed.value) {
//alert("dd"+userType.value); //alert("dd"+userType.value);
if (userType.value === 'user') { if (userType.value === 'user') {
...@@ -583,24 +588,21 @@ ...@@ -583,24 +588,21 @@
const handleChange = (value: string) => { const handleChange = (value: string) => {
console.log(`selected ${value}`); console.log(`selected ${value}`);
nextNodeNameSelevue.value=value; nextNodeNameSelevue.value = value;
console.log(" nextNodeNameSelevue.value ",nextNodeNameSelevue.value); console.log(' nextNodeNameSelevue.value ', nextNodeNameSelevue.value);
if(value==0){ if (value == 0) {
ChangeSelectNodeAfter(res0); ChangeSelectNodeAfter(res0);
} }
if(value==1){ if (value == 1) {
ChangeSelectNodeAfter(res1); ChangeSelectNodeAfter(res1);
} }
if(value==2){ if (value == 2) {
ChangeSelectNodeAfter(res2); ChangeSelectNodeAfter(res2);
} }
}; };
function ChangeSelectNodeAfter(res) { function ChangeSelectNodeAfter(res) {
isFixed.value = res.dataType == 'fixed' ? true : false; isFixed.value = res.dataType == 'fixed' ? true : false;
if (isFixed.value && !isApproval.value) { if (isFixed.value && !isApproval.value) {
showApprovalUi.value; showApprovalUi.value;
...@@ -609,8 +611,6 @@ ...@@ -609,8 +611,6 @@
nextNode.value = res; nextNode.value = res;
nextNodeName.value = res.userTask?.name || ''; nextNodeName.value = res.userTask?.name || '';
//alert(""+res.dataType);
if (res.type === 'role') { if (res.type === 'role') {
removeSchemaByFiled('checkSendUser'); removeSchemaByFiled('checkSendUser');
if (res.dataType === 'fixed' && res.userTask?.candidateGroups) { if (res.dataType === 'fixed' && res.userTask?.candidateGroups) {
...@@ -626,16 +626,13 @@ ...@@ -626,16 +626,13 @@
}); });
} }
} }
} }
const iniData = async (data: any) => { const iniData = async (data: any) => {
try { try {
// 设置工作流数据 // 设置工作流数据
workFlowData.value = data; workFlowData.value = data;
console.log("why iniData",data); console.log('why iniData', data);
// 更新任务表单 // 更新任务表单
Object.assign(taskForm, { Object.assign(taskForm, {
...@@ -658,58 +655,49 @@ ...@@ -658,58 +655,49 @@
if (taskForm.taskId) { if (taskForm.taskId) {
const reslist = await getNextFlowNode({ taskId: taskForm.taskId }); const reslist = await getNextFlowNode({ taskId: taskForm.taskId });
console.log('getNextFlowNode reslist ', reslist);
console.log("getNextFlowNode reslist ",reslist);
if (reslist == null) { if (reslist == null) {
// 到达流程结束节点:设置展示状态但不要提前返回,仍需加载表单和流转记录
nextNodeName.value = '结束'; nextNodeName.value = '结束';
removeSchemaByFiled('checkSendRole'); removeSchemaByFiled('checkSendRole');
removeSchemaByFiled('checkSendUser'); removeSchemaByFiled('checkSendUser');
showApprovalUi.value = isApproval.value; showApprovalUi.value = isApproval.value;
shownextNodeNameSele.value =false; shownextNodeNameSele.value = false;
shownextNodeNametext.value =true; shownextNodeNametext.value = true;
return false; // 不再 return,这样后续仍会继续加载流转记录和表单
}else{ } else {
shownextNodeNameSele.value =true; shownextNodeNameSele.value = true;
shownextNodeNametext.value =false; shownextNodeNametext.value = false;
nextNodenum.value=reslist.length;
nextNodenum.value = reslist.length;
//why工作流修改 //why工作流修改
// 使用 for 循环赋值 // 使用 for 循环赋值
for (let i = 0; i <reslist.length; i++) { for (let i = 0; i < reslist.length; i++) {
let resb = reslist[i]; let resb = reslist[i];
tableOptions.value.push({ tableOptions.value.push({
id: `${i}`, id: `${i}`,
name: `${resb.userTask.name}`, name: `${resb.userTask.name}`,
value: `${i}` value: `${i}`,
}); });
} }
nextNodeNameSele.value = tableOptions.value[0].name; nextNodeNameSele.value = tableOptions.value[0].name;
// tableOptions.value = res.userTaskList;
const res = reslist[0]; // 获取第一行数据 const res = reslist[0]; // 获取第一行数据
console.log("res ",res); console.log('res ', res);
ChangeSelectNodeAfter(res); ChangeSelectNodeAfter(res);
if(reslist.length==1){ if (reslist.length == 1) {
res0.value = reslist[0]; res0.value = reslist[0];
} }
if(reslist.length==2){ if (reslist.length == 2) {
res0.value = reslist[0]; res0.value = reslist[0];
res1.value = reslist[1]; res1.value = reslist[1];
} }
if(reslist.length==3){ if (reslist.length == 3) {
res0.value = reslist[0]; res0.value = reslist[0];
res1.value = reslist[1]; res1.value = reslist[1];
res2.value = reslist[2]; res2.value = reslist[2];
} }
} }
} }
...@@ -732,7 +720,28 @@ ...@@ -732,7 +720,28 @@
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* ==================== 柔和中性风格 - CSS 变量 ==================== */
.execute-form-container {
background: var(--color-bg-white);
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
.app-container { .app-container {
--color-primary: #3b5bdb;
--color-primary-hover: #364fc7;
--color-primary-light: #e8ecfd;
--color-success: #2f9e44;
--color-warning: #e67700;
--color-error: #c92a2a;
--color-text-primary: #1c1c1e;
--color-text-secondary: #555770;
--color-text-muted: #a0a3b1;
--color-border: #e4e4e9;
--color-border-strong: #c8c8d0;
--color-bg-page: #f5f5f7;
--color-bg-white: #ffffff;
--color-bg-section: #f0f0f4;
--radius: 6px;
height: 100vh; height: 100vh;
margin: 0; margin: 0;
padding: 0; padding: 0;
......
<template> <template>
<div> <div>
<vxe-drawer <vxe-drawer
...@@ -9,21 +8,40 @@ ...@@ -9,21 +8,40 @@
width="100%" width="100%"
height="100%" height="100%"
:loading="loading" :loading="loading"
class="flat-flow-drawer"
> >
<template #header>
<div class="drawer-header">
<h3 class="drawer-title">{{ pageTilte }}</h3>
<span class="drawer-subtitle">计划审批流程</span>
</div>
</template>
<div class="drawer-body">
<div class="iframe-container">
<div v-if="loading" class="loading-overlay">
<div class="loading-content">
<a-spin size="large" />
<span class="loading-text">加载流程中...</span>
</div>
</div>
<iframe <iframe
id="iframeId" id="iframeId"
ref="iframeRef" ref="iframeRef"
:src="frmUrl" :src="frmUrl"
frameborder="0" frameborder="0"
style="width: 100%; height: 100%;" class="flow-iframe"
@load="handleIframeLoad"
></iframe> ></iframe>
</div>
</div>
</vxe-drawer> </vxe-drawer>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted,nextTick } from "vue"; import { ref, onMounted, nextTick } from "vue";
import { useUserStoreWithOut } from "/@/store/modules/user"; import { useUserStoreWithOut } from "/@/store/modules/user";
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import { getToken } from '/@/utils/auth'; import { getToken } from '/@/utils/auth';
...@@ -35,18 +53,190 @@ const loading = ref(false); ...@@ -35,18 +53,190 @@ const loading = ref(false);
const pageTilte = ref(""); const pageTilte = ref("");
const iframeRef = ref<HTMLIFrameElement>(); const iframeRef = ref<HTMLIFrameElement>();
const handleIframeLoad = () => {
loading.value = false;
};
const iniPage = async (data) => { const iniPage = async (data) => {
pageTilte.value = data.projectName; pageTilte.value = data.projectName;
showPopup.value = true; showPopup.value = true;
loading.value = true;
frmUrl.value = `${import.meta.env.VITE_APP_JFLOW_CORE_ADDR}/#/WF/MyFlow?FlowNo=087&Token=${user.getJflowToken}&tid=${data.id}`; frmUrl.value = `${import.meta.env.VITE_APP_JFLOW_CORE_ADDR}/#/WF/MyFlow?FlowNo=087&Token=${user.getJflowToken}&tid=${data.id}`;
const setSourctUrl = '/api/jflow/setCCWorkId'; const setSourctUrl = '/api/jflow/setCCWorkId';
await defHttp.get({ await defHttp.get({
url: setSourctUrl, url: setSourctUrl,
params: {"targetId":data.id,"targetKey":"targetKey","token":getToken()}, params: {"targetId":data.id,"targetKey":"targetKey","token":getToken()},
}); });
} }
// 暴露方法 // 暴露方法
defineExpose({ iniPage }); defineExpose({ iniPage });
</script> </script>
<style scoped lang="less">
/* ==================== 柔和中性风格 - CSS 变量 ==================== */
.flat-flow-drawer {
--color-primary: #3b5bdb;
--color-primary-hover: #364fc7;
--color-primary-light: #e8ecfd;
--color-success: #2f9e44;
--color-warning: #e67700;
--color-error: #c92a2a;
--color-text-primary: #1c1c1e;
--color-text-secondary: #555770;
--color-text-muted: #a0a3b1;
--color-border: #e4e4e9;
--color-border-strong: #c8c8d0;
--color-bg-page: #f5f5f7;
--color-bg-white: #ffffff;
--color-bg-section: #f0f0f4;
--radius: 6px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
/* ==================== 抽屉头部样式 ==================== */
.drawer-header {
display: flex;
align-items: baseline;
gap: 10px;
border-left: 3px solid var(--color-primary);
padding-left: 10px;
}
.drawer-title {
font-size: 16px;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
letter-spacing: -0.2px;
}
.drawer-subtitle {
font-size: 12px;
color: var(--color-text-muted);
}
/* ==================== 抽屉主体样式 ==================== */
.drawer-body {
height: 100%;
background: var(--color-bg-page);
padding: 12px;
}
.iframe-container {
position: relative;
width: 100%;
height: 100%;
background: var(--color-bg-white);
border: 1px solid var(--color-border);
border-radius: var(--radius);
overflow: hidden;
}
.flow-iframe {
width: 100%;
height: 100%;
border: none;
}
/* ==================== 加载状态 ==================== */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.95);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.loading-text {
font-size: 14px;
color: var(--color-text-secondary);
}
/* ==================== VXE Drawer 样式覆盖 ==================== */
:deep(.vxe-drawer--wrapper) {
.vxe-drawer--header {
background: var(--color-bg-section);
border-bottom: 1px solid var(--color-border);
padding: 14px 24px;
}
.vxe-drawer--body {
padding: 0;
background: var(--color-bg-page);
}
.vxe-drawer--footer {
background: var(--color-bg-white);
border-top: 1px solid var(--color-border);
padding: 12px 24px;
}
.vxe-button {
border-radius: var(--radius);
font-weight: 500;
font-size: 13px;
box-shadow: none;
&.type--primary {
background: var(--color-primary);
border-color: var(--color-primary);
&:hover {
background: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
}
&.type--default {
border-color: var(--color-border-strong);
color: var(--color-text-secondary);
background: var(--color-bg-white);
&:hover {
border-color: var(--color-primary);
color: var(--color-primary);
background: var(--color-primary-light);
}
}
}
}
/* ==================== 响应式设计 ==================== */
@media (max-width: 768px) {
.drawer-header {
flex-direction: column;
gap: 4px;
}
.drawer-title {
font-size: 16px;
}
.drawer-body {
padding: 12px;
}
:deep(.vxe-drawer--wrapper) {
.vxe-drawer--header {
padding: 12px 16px;
}
.vxe-drawer--footer {
padding: 10px 16px;
}
}
}
</style>
<template> <template>
<div style="background-color: #fff; padding: 100px"> <div class="plan-form-container">
<a-spin :spinning="loading"> <a-spin :spinning="loading">
<!-- 表单头部 -->
<div class="form-header">
<h2 class="form-title">计划详情</h2>
<p class="form-subtitle">查看和编辑计划信息</p>
</div>
<!-- 表单内容 -->
<div class="form-body">
<BasicForm @register="registerForm"> <BasicForm @register="registerForm">
<template #planBasis="{ model, field }"> <template #planBasis="{ model, field }">
<div v-if="model[field]"> <div class="basis-container" v-if="model[field]">
<div v-if="isValidJson(model[field])"> <div v-if="isValidJson(model[field])" class="basis-tags">
<a-tag <a-tag
v-for="item in safeJsonParse(model[field])" v-for="item in safeJsonParse(model[field])"
@click="viewBasisDetail(item)" @click="viewBasisDetail(item)"
:key="item.id" :key="item.id"
style="margin-bottom: 8px; cursor: pointer" class="basis-tag"
> >
{{ item.name }} {{ item.name }}
</a-tag> </a-tag>
</div> </div>
<a-alert v-else type="warning" :message="`无效的数据格式: ${model[field]}`" /> <a-alert v-else type="warning" :message="`无效的数据格式: ${model[field]}`" class="basis-alert" />
</div>
<div v-else class="basis-empty">
<a-empty description="暂无依据数据" :image-style="{ height: '40px' }" />
</div> </div>
<a-empty v-else description="暂无数据" />
</template> </template>
</BasicForm> </BasicForm>
</a-spin> </div>
<div style="width: 100%; text-align: center; margin-top: 24px" v-if="!formDisabled"> <!-- 操作按钮 -->
<a-space> <div class="form-footer" v-if="!formDisabled">
<a-button @click="submitForm" type="primary" :loading="submitting" pre-icon="ant-design:check">提 交</a-button> <a-space :size="12">
<a-button @click="handleReset">重 置</a-button> <a-button @click="handleReset">重置</a-button>
<a-button @click="submitForm" type="primary" :loading="submitting">提交</a-button>
</a-space> </a-space>
</div> </div>
</a-spin>
<AuditInnerDetailDrawer ref="auditInnerDetailDrawerRef" :visible="showDetailDrawer" :basis="selectedBasis" @close="handleDrawerClose" /> <AuditInnerDetailDrawer ref="auditInnerDetailDrawerRef" :visible="showDetailDrawer" :basis="selectedBasis" @close="handleDrawerClose" />
</div> </div>
...@@ -125,29 +137,12 @@ ...@@ -125,29 +137,12 @@
const initFormData = async () => { const initFormData = async () => {
try { try {
loading.value = true; loading.value = true;
//const { cctoken, WorkID } = getUrlParams();
//console.log('Token:', cctoken, 'WorkID:', WorkID);
const timestamp = new Date().getTime(); const timestamp = new Date().getTime();
/**
const gettokeyUrl = '/api/jflow/getCCWorkTokenAndTid';
const {tid,token} = await defHttp.get({
url: gettokeyUrl,
params: {
"targetKey":"targetKey",
"flowToken":cctoken,
"WorkID":WorkID,
"_t": timestamp},
});
*/
let tid = toRaw(route.query).id; let tid = toRaw(route.query).id;
console.log('tid:', tid); console.log('tid:', tid);
//setAuthCache(TOKEN_KEY, token);
//console.log('tid:', tid, 'token:', token);
const queryByIdUrl = '/plan.main/stPlanMan/queryById'; const queryByIdUrl = '/plan.main/stPlanMan/queryById';
const data = await defHttp.get({ const data = await defHttp.get({
url: queryByIdUrl, url: queryByIdUrl,
...@@ -169,14 +164,216 @@ ...@@ -169,14 +164,216 @@
loading.value = false; loading.value = false;
} }
}; };
const handleReset = () => {
resetFields();
};
const submitForm = async () => {
try {
submitting.value = true;
await validate();
const values = getFieldsValue();
await saveOrUpdate(values, true);
createMessage.success('提交成功');
} catch (error) {
console.error('提交失败:', error);
} finally {
submitting.value = false;
}
};
onMounted(() => { onMounted(() => {
initFormData(); initFormData();
}); });
</script> </script>
<style scoped> <style scoped lang="less">
.ant-tag { /* ==================== 柔和中性风格 - CSS 变量 ==================== */
margin-right: 8px; .plan-form-container {
--color-primary: #3b5bdb;
--color-primary-hover: #364fc7;
--color-primary-light: #e8ecfd;
--color-success: #2f9e44;
--color-warning: #e67700;
--color-error: #c92a2a;
--color-text-primary: #1c1c1e;
--color-text-secondary: #555770;
--color-text-muted: #a0a3b1;
--color-border: #e4e4e9;
--color-border-strong: #c8c8d0;
--color-bg-page: #f5f5f7;
--color-bg-white: #ffffff;
--color-bg-section: #f0f0f4;
--radius: 6px;
background: var(--color-bg-white);
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
/* ==================== 表单头部 ==================== */
.form-header {
background: var(--color-bg-white);
border-bottom: 1px solid var(--color-border);
padding: 20px 32px;
border-left: 3px solid var(--color-primary);
padding-left: 28px;
}
.form-title {
font-size: 18px;
font-weight: 600;
color: var(--color-text-primary);
margin: 0 0 3px 0;
letter-spacing: -0.2px;
}
.form-subtitle {
font-size: 13px;
color: var(--color-text-muted);
margin: 0;
}
/* ==================== 表单主体 ==================== */
.form-body {
padding: 24px 32px;
background: var(--color-bg-white);
}
/* 依据标签样式 */
.basis-container {
padding: 8px 0;
}
.basis-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.basis-tag {
display: inline-flex;
align-items: center;
padding: 4px 10px;
background: var(--color-primary-light);
border: 1px solid var(--color-primary);
border-radius: var(--radius);
color: var(--color-primary);
font-size: 13px;
cursor: pointer; cursor: pointer;
transition: background 0.15s;
&:hover {
background: #d0d9f8;
}
}
.basis-alert {
border-radius: var(--radius);
}
.basis-empty {
padding: 16px;
background: var(--color-bg-section);
border-radius: var(--radius);
border: 1px dashed var(--color-border-strong);
}
/* ==================== 表单底部按钮 ==================== */
.form-footer {
padding: 14px 32px;
background: var(--color-bg-section);
border-top: 1px solid var(--color-border);
display: flex;
justify-content: flex-end;
}
/* ==================== 表单控件样式覆盖 ==================== */
:deep(.ant-form-item) {
margin-bottom: 18px;
}
:deep(.ant-form-item-label > label) {
font-size: 13px;
font-weight: 500;
color: var(--color-text-secondary);
}
:deep(.ant-input),
:deep(.ant-select-selector),
:deep(.ant-picker),
:deep(.ant-input-textarea textarea) {
border-radius: var(--radius) !important;
border-color: var(--color-border) !important;
font-size: 13px;
box-shadow: none !important;
&:hover {
border-color: var(--color-border-strong) !important;
}
&:focus,
&.ant-input-focused {
border-color: var(--color-primary) !important;
box-shadow: 0 0 0 2px var(--color-primary-light) !important;
}
}
:deep(.ant-select-focused .ant-select-selector) {
border-color: var(--color-primary) !important;
box-shadow: 0 0 0 2px var(--color-primary-light) !important;
}
/* ==================== 按钮样式 ==================== */
:deep(.ant-btn) {
border-radius: var(--radius);
font-weight: 500;
font-size: 13px;
height: 34px;
padding: 0 18px;
box-shadow: none;
&.ant-btn-primary {
background: var(--color-primary);
border-color: var(--color-primary);
&:hover {
background: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
}
&.ant-btn-default {
border-color: var(--color-border-strong);
color: var(--color-text-secondary);
background: var(--color-bg-white);
&:hover {
border-color: var(--color-primary);
color: var(--color-primary);
background: var(--color-primary-light);
}
}
}
/* ==================== 加载状态 ==================== */
:deep(.ant-spin-container) {
min-height: 400px;
}
/* ==================== 响应式设计 ==================== */
@media (max-width: 768px) {
.form-header {
padding: 16px 20px;
}
.form-body {
padding: 20px;
}
.form-footer {
padding: 12px 20px;
} }
}
</style> </style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论