feat(admin): 选人组件接入——UserCheckModal 加单选模式+UserSelectInput 单选封装;映射办理人/审批节点指定用户弃手输 ID 改弹窗选人 (/admin/user/tree);删休眠 UserCheckInput 死代码
build / build (push) Successful in 1m56s
build / build (push) Successful in 1m56s
This commit is contained in:
@@ -124,6 +124,10 @@ class FlowApi {
|
|||||||
mapLookupUser(userId: number) {
|
mapLookupUser(userId: number) {
|
||||||
return get(`${ROOT}/admin/map/lookupUser`, { userId });
|
return get(`${ROOT}/admin/map/lookupUser`, { userId });
|
||||||
}
|
}
|
||||||
|
/** 选人组件数据源:学校组树(组→组内用户叶子) */
|
||||||
|
adminUserTree() {
|
||||||
|
return get(`${ROOT}/admin/user/tree`);
|
||||||
|
}
|
||||||
/** 一键拉取教师组·预览(学校子树教师组容器 → 科室+组内成员) */
|
/** 一键拉取教师组·预览(学校子树教师组容器 → 科室+组内成员) */
|
||||||
mapGroupPreview() {
|
mapGroupPreview() {
|
||||||
return get(`${ROOT}/admin/map/groupPreview`);
|
return get(`${ROOT}/admin/map/groupPreview`);
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- 通知对象选择:全部用户 / 指定用户(自旧前端 user_check_input.vue 适配) -->
|
|
||||||
<div class="check-user-wrap">
|
|
||||||
<div class="check-user-box">
|
|
||||||
<RadioGroup v-model="isCheckUserAll" class="radio" @on-change="userAllCheck">
|
|
||||||
<Radio :disabled="editDisabled" :label="0">{{ isEdit ? "现有" : "全部" }}用户</Radio>
|
|
||||||
<Radio :label="1">指定用户</Radio>
|
|
||||||
</RadioGroup>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
v-model="userNameStr"
|
|
||||||
readonly
|
|
||||||
:disabled="!isCheckUserAll"
|
|
||||||
placeholder="选择指定的用户"
|
|
||||||
class="input"
|
|
||||||
></Input>
|
|
||||||
<Button icon="md-add" @click="checkUserShow" :disabled="!isCheckUserAll"></Button>
|
|
||||||
<Button icon="md-trash" @click="resetUser" :disabled="!isCheckUserAll"></Button>
|
|
||||||
</div>
|
|
||||||
<UserCheckModal
|
|
||||||
ref="userCheckModal"
|
|
||||||
:sources="sources"
|
|
||||||
:userids="tempUserIds"
|
|
||||||
@on-submit="submitUser"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
/*
|
|
||||||
* 通知对象选择输入(自旧前端 oldfront/src/components/user_check_input.vue 适配 Vue3)。
|
|
||||||
* v-model 值为 "all" 或用户 id 逗号串;切换到「指定用户」直接弹出选择用户弹窗。
|
|
||||||
*/
|
|
||||||
import UserCheckModal from "@/components/UserCheckModal.vue";
|
|
||||||
import { baseApi, settingApi } from "@/api/module/notice";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "UserCheckInput",
|
|
||||||
components: { UserCheckModal },
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: "all",
|
|
||||||
},
|
|
||||||
/** 编辑态:全部用户选项禁用为「现有用户」 */
|
|
||||||
isEdit: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ["update:modelValue"],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isCheckUserAll: 0 as number,
|
|
||||||
tempUserIds: [] as any[],
|
|
||||||
userNameStr: "",
|
|
||||||
editDisabled: false,
|
|
||||||
sources: [
|
|
||||||
{
|
|
||||||
name: "基础平台",
|
|
||||||
loader: () => baseApi.getSchoolAll().send() as Promise<any[]>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "自定义组",
|
|
||||||
loader: () => settingApi.getGroupTree().send() as Promise<any[]>,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
if (this.modelValue === "all") {
|
|
||||||
this.isCheckUserAll = 0;
|
|
||||||
this.userNameStr = this.isEdit ? "现有用户" : "全部用户";
|
|
||||||
} else {
|
|
||||||
this.isCheckUserAll = 1;
|
|
||||||
this.tempUserIds = this.modelValue.split(",").map((x) => parseInt(x)).filter((x) => !isNaN(x));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
checkUserShow() {
|
|
||||||
(this.$refs.userCheckModal as any).show();
|
|
||||||
},
|
|
||||||
resetUser() {
|
|
||||||
//清除用户
|
|
||||||
this.tempUserIds = [];
|
|
||||||
this.userNameStr = "";
|
|
||||||
this.$emit("update:modelValue", "");
|
|
||||||
},
|
|
||||||
userAllCheck(type: number) {
|
|
||||||
let tempIds: any = String(this.modelValue || "")
|
|
||||||
.split(",")
|
|
||||||
.map((x) => parseInt(x))
|
|
||||||
.filter((x) => !isNaN(x));
|
|
||||||
if (type == 0) {
|
|
||||||
if (this.isEdit) {
|
|
||||||
this.tempUserIds = tempIds;
|
|
||||||
this.userNameStr = "现有用户";
|
|
||||||
this.$emit("update:modelValue", String(tempIds));
|
|
||||||
} else {
|
|
||||||
this.tempUserIds = [];
|
|
||||||
this.userNameStr = "全部用户";
|
|
||||||
this.$emit("update:modelValue", "all");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.isEdit) {
|
|
||||||
this.editDisabled = true;
|
|
||||||
}
|
|
||||||
this.tempUserIds = tempIds;
|
|
||||||
this.userNameStr = "";
|
|
||||||
this.checkUserShow();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
submitUser(arr: any[]) {
|
|
||||||
//确认用户
|
|
||||||
this.userNameStr = arr.map((x) => x.name).join("、");
|
|
||||||
const ids = arr.map((x) => x.id).join(",");
|
|
||||||
this.$emit("update:modelValue", ids || "all");
|
|
||||||
if (!arr.length) {
|
|
||||||
// 清空后回到全部用户
|
|
||||||
this.isCheckUserAll = 0;
|
|
||||||
this.userNameStr = "全部用户";
|
|
||||||
this.$emit("update:modelValue", "all");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
modelValue(val: string) {
|
|
||||||
if (val === "all") {
|
|
||||||
this.isCheckUserAll = 0;
|
|
||||||
this.userNameStr = this.isEdit ? "现有用户" : "全部用户";
|
|
||||||
} else if (val === "") {
|
|
||||||
this.userNameStr = "";
|
|
||||||
} else {
|
|
||||||
// 编辑回显:指定用户 id 串
|
|
||||||
this.isCheckUserAll = 1;
|
|
||||||
this.tempUserIds = val
|
|
||||||
.split(",")
|
|
||||||
.map((x) => parseInt(x))
|
|
||||||
.filter((x) => !isNaN(x));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.check-user-wrap {
|
|
||||||
width: 100%;
|
|
||||||
.check-user-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
.radio {
|
|
||||||
width: 180px;
|
|
||||||
flex: none;
|
|
||||||
font-size: 12px;
|
|
||||||
vertical-align: middle;
|
|
||||||
line-height: 32px;
|
|
||||||
}
|
|
||||||
.input {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -51,6 +51,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-panel fl">
|
<div class="user-panel fl">
|
||||||
|
<template v-if="multiple">
|
||||||
<div class="title-box fix">
|
<div class="title-box fix">
|
||||||
<Checkbox @on-change="checkAllUser" v-model="isCheckUserAll">全选</Checkbox>
|
<Checkbox @on-change="checkAllUser" v-model="isCheckUserAll">全选</Checkbox>
|
||||||
<div class="fr">{{ tempUserList.length }}</div>
|
<div class="fr">{{ tempUserList.length }}</div>
|
||||||
@@ -72,6 +73,15 @@
|
|||||||
<span class="info">{{ item.group }}</span>
|
<span class="info">{{ item.group }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="title-box fix"><b>已选择</b></div>
|
||||||
|
<div class="single-box" v-if="selectedUser">
|
||||||
|
{{ selectedUser.name }}
|
||||||
|
<span class="info">{{ selectedUser.group }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="single-empty" v-else>在左侧树中点击选中一名用户</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -112,6 +122,11 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
/** 单选模式:树上点击叶子即选定(flow-center 办理人映射等单选场景);默认多选与 Notice 口径一致 */
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ["update:userids", "on-submit"],
|
emits: ["update:userids", "on-submit"],
|
||||||
data() {
|
data() {
|
||||||
@@ -119,6 +134,9 @@ export default {
|
|||||||
ztreeDomId: "user_check_tree_" + Math.floor(Math.random() * 1e8),
|
ztreeDomId: "user_check_tree_" + Math.floor(Math.random() * 1e8),
|
||||||
modal: false,
|
modal: false,
|
||||||
tabIndex: 0,
|
tabIndex: 0,
|
||||||
|
/** 单选模式当前选中(每次打开重置) */
|
||||||
|
selectedUser: null as any,
|
||||||
|
tabIndex: 0,
|
||||||
/** 每次打开弹窗自增,强制重建 Tabs,避免上次 tab 高亮残留与数据错位 */
|
/** 每次打开弹窗自增,强制重建 Tabs,避免上次 tab 高亮残留与数据错位 */
|
||||||
tabKey: 0,
|
tabKey: 0,
|
||||||
/** 树加载序号:tab 快速切换时只渲染最后一次请求的结果 */
|
/** 树加载序号:tab 快速切换时只渲染最后一次请求的结果 */
|
||||||
@@ -146,6 +164,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
show() {
|
show() {
|
||||||
this.modal = true;
|
this.modal = true;
|
||||||
|
this.selectedUser = null;
|
||||||
// 重置到第一个 tab:显式赋值 + key 重建 Tabs,保证高亮与数据一致
|
// 重置到第一个 tab:显式赋值 + key 重建 Tabs,保证高亮与数据一致
|
||||||
this.tabIndex = 0;
|
this.tabIndex = 0;
|
||||||
this.tabKey++;
|
this.tabKey++;
|
||||||
@@ -175,9 +194,19 @@ export default {
|
|||||||
},
|
},
|
||||||
handleSelect(query: string) {
|
handleSelect(query: string) {
|
||||||
if (query.indexOf(",") != -1) query = query.split(",")[0];
|
if (query.indexOf(",") != -1) query = query.split(",")[0];
|
||||||
|
const findObj = this.currentTreeFlatten.find(
|
||||||
|
(x: any) => x.type == 2 && x.name == query.split(",")[0]
|
||||||
|
);
|
||||||
|
if (!findObj) return;
|
||||||
|
if (this.multiple) {
|
||||||
var treeObj = ($.fn as any).zTree.getZTreeObj(this.ztreeDomId);
|
var treeObj = ($.fn as any).zTree.getZTreeObj(this.ztreeDomId);
|
||||||
var node = treeObj.getNodeByParam("name", query, null);
|
var node = treeObj.getNodeByParam("name", query, null);
|
||||||
if (!!node) treeObj.checkNode(node, true, true, true);
|
if (!!node) treeObj.checkNode(node, true, true, true);
|
||||||
|
} else {
|
||||||
|
// 单选:搜索命中即选定并直接提交(提交后可再清空)
|
||||||
|
this.selectedUser = { ...findObj };
|
||||||
|
this.submit();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* ztree 数据预处理:拼接 pid 保证同人多组时 id 可区分;叶子标记 type=2。
|
* ztree 数据预处理:拼接 pid 保证同人多组时 id 可区分;叶子标记 type=2。
|
||||||
@@ -240,7 +269,7 @@ export default {
|
|||||||
showIcon: true,
|
showIcon: true,
|
||||||
},
|
},
|
||||||
check: {
|
check: {
|
||||||
enable: true,
|
enable: this.multiple,
|
||||||
chkboxType: { Y: "ps", N: "ps" },
|
chkboxType: { Y: "ps", N: "ps" },
|
||||||
},
|
},
|
||||||
callback: {},
|
callback: {},
|
||||||
@@ -283,8 +312,22 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
treeSetting.callback.onClick = (event: any, treeId: string, treeNode: any) => {
|
treeSetting.callback.onClick = (event: any, treeId: string, treeNode: any) => {
|
||||||
let check = treeNode.checked;
|
|
||||||
let treeObj = ($.fn as any).zTree.getZTreeObj(treeId);
|
let treeObj = ($.fn as any).zTree.getZTreeObj(treeId);
|
||||||
|
if (!_this.multiple) {
|
||||||
|
// 单选:叶子节点点击即选定;组节点仅展开(关闭复选,无勾选语义)
|
||||||
|
if (treeNode.type == 2) {
|
||||||
|
treeObj.selectNode(treeNode, false, false);
|
||||||
|
_this.selectedUser = {
|
||||||
|
id: _this.getSplitId(treeNode.id),
|
||||||
|
name: treeNode.name,
|
||||||
|
group: treeNode.group || "",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
treeObj.expandNode(treeNode, !treeNode.open, false, false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let check = treeNode.checked;
|
||||||
treeObj.checkNode(treeNode, !check, true);
|
treeObj.checkNode(treeNode, !check, true);
|
||||||
formatUser(treeNode, treeNode.checked);
|
formatUser(treeNode, treeNode.checked);
|
||||||
};
|
};
|
||||||
@@ -333,6 +376,17 @@ export default {
|
|||||||
},
|
},
|
||||||
submit() {
|
submit() {
|
||||||
const _this = this;
|
const _this = this;
|
||||||
|
if (!_this.multiple) {
|
||||||
|
if (!_this.selectedUser) {
|
||||||
|
Message.warning("请先选择用户");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const u: any = { id: _this.selectedUser.id, name: _this.selectedUser.name, group: _this.selectedUser.group };
|
||||||
|
this.$emit("update:userids", [u.id]);
|
||||||
|
this.$emit("on-submit", [u]);
|
||||||
|
this.modal = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
let idsArr = this.tempUserList.map((x: any) => _this.getSplitId(x.id));
|
let idsArr = this.tempUserList.map((x: any) => _this.getSplitId(x.id));
|
||||||
idsArr = Array.from(new Set(idsArr));
|
idsArr = Array.from(new Set(idsArr));
|
||||||
this.$emit("update:userids", idsArr);
|
this.$emit("update:userids", idsArr);
|
||||||
@@ -412,6 +466,21 @@ export default {
|
|||||||
background: #e4edf6;
|
background: #e4edf6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.single-box {
|
||||||
|
padding: 12px;
|
||||||
|
line-height: 32px;
|
||||||
|
.info {
|
||||||
|
float: right;
|
||||||
|
margin-right: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.single-empty {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #c5c8ce;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.arrow-box {
|
.arrow-box {
|
||||||
width: 128px;
|
width: 128px;
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 单选用户输入(参照 Notice 自定义组选人范式):readonly Input + 选择按钮 + 清空;
|
||||||
|
v-model=用户 id;display-name 回显姓名快照(调用方已知姓名时随值下传) -->
|
||||||
|
<div class="sel-user-box">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
v-model="nameText"
|
||||||
|
readonly
|
||||||
|
placeholder="点击「选择」指定用户"
|
||||||
|
class="input"
|
||||||
|
></Input>
|
||||||
|
<Button icon="md-add" @click="openModal">选择</Button>
|
||||||
|
<Button icon="md-trash" v-if="modelValue" @click="reset"></Button>
|
||||||
|
<UserCheckModal
|
||||||
|
ref="userCheckModal"
|
||||||
|
:sources="sources"
|
||||||
|
:multiple="false"
|
||||||
|
@on-submit="onSubmit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import UserCheckModal from "@/components/UserCheckModal.vue";
|
||||||
|
import { flowApi } from "@/api/module/flow";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "UserSelectInput",
|
||||||
|
components: { UserCheckModal },
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
/** 当前 id 的姓名快照(编辑回显/选中即得,避免 #id 兜底) */
|
||||||
|
displayName: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["update:modelValue", "update:displayName", "on-change"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nameText: this.displayName,
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
name: "基础平台",
|
||||||
|
loader: () => flowApi.adminUserTree().send() as Promise<any[]>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openModal() {
|
||||||
|
(this.$refs.userCheckModal as any).show();
|
||||||
|
},
|
||||||
|
/** 单选弹窗提交:数组恒为 1 条(多选复用同组件) */
|
||||||
|
onSubmit(arr: any[]) {
|
||||||
|
if (!arr || !arr.length) return;
|
||||||
|
const u = arr[0];
|
||||||
|
this.nameText = u.name + "(" + u.id + ")";
|
||||||
|
this.$emit("update:modelValue", u.id);
|
||||||
|
this.$emit("update:displayName", u.name);
|
||||||
|
this.$emit("on-change", u);
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.nameText = "";
|
||||||
|
this.$emit("update:modelValue", null);
|
||||||
|
this.$emit("update:displayName", "");
|
||||||
|
this.$emit("on-change", null);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(val: number) {
|
||||||
|
// 外部清空/重置时同步清姓名
|
||||||
|
if (!val) this.nameText = "";
|
||||||
|
else if (!this.nameText) this.nameText = "#" + val;
|
||||||
|
},
|
||||||
|
displayName(val: string) {
|
||||||
|
this.nameText = val;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.sel-user-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 办理人映射管理(概览 §4.7/§六):DEPT_KEY → 办理人;成员检索经 UserBase /oapi/user/byId 补姓名
|
// 办理人映射管理(概览 §4.7/§六):DEPT_KEY → 办理人;办理人改为弹窗选人(参照 Notice 选人范式)
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import { Message } from "view-ui-plus";
|
import { Message } from "view-ui-plus";
|
||||||
|
import UserSelectInput from "@/components/UserSelectInput.vue";
|
||||||
import { flowApi } from "@/api/module/flow";
|
import { flowApi } from "@/api/module/flow";
|
||||||
import Utils from "@/utils";
|
import Utils from "@/utils";
|
||||||
|
|
||||||
@@ -12,7 +13,6 @@ const rows = ref<any[]>([]);
|
|||||||
const saveVisible = ref(false);
|
const saveVisible = ref(false);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
const form = ref<any>({ deptKey: "", deptName: "", userId: null, userName: "" });
|
const form = ref<any>({ deptKey: "", deptName: "", userId: null, userName: "" });
|
||||||
const lookingUp = ref(false);
|
|
||||||
|
|
||||||
// 一键拉取教师组(预览确认后批量导入)
|
// 一键拉取教师组(预览确认后批量导入)
|
||||||
const groupVisible = ref(false);
|
const groupVisible = ref(false);
|
||||||
@@ -79,26 +79,14 @@ function onAdd() {
|
|||||||
saveVisible.value = true;
|
saveVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onLookup() {
|
function onUserPick(u: any) {
|
||||||
if (!form.value.userId) {
|
// 选人组件回传 id+name 同帧快照(姓名不再自查)
|
||||||
Message.warning("请先填用户ID");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lookingUp.value = true;
|
|
||||||
try {
|
|
||||||
const u: any = await flowApi.mapLookupUser(form.value.userId).send();
|
|
||||||
form.value.userName = u?.name || "";
|
form.value.userName = u?.name || "";
|
||||||
Message.success(`检索到用户:${u?.name || "-"}`);
|
|
||||||
} catch {
|
|
||||||
Message.error("UserBase 检索失败");
|
|
||||||
} finally {
|
|
||||||
lookingUp.value = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSave() {
|
async function onSave() {
|
||||||
if (!form.value.deptKey.trim() || !form.value.userId) {
|
if (!form.value.deptKey.trim() || !form.value.userId) {
|
||||||
Message.warning("科室编码与用户ID必填");
|
Message.warning("科室编码与办理人必填");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
@@ -144,12 +132,14 @@ onMounted(load);
|
|||||||
<FormItem label="科室名称">
|
<FormItem label="科室名称">
|
||||||
<Input v-model="form.deptName" placeholder="如 医保办(可空)" />
|
<Input v-model="form.deptName" placeholder="如 医保办(可空)" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="用户ID">
|
<FormItem label="办理人">
|
||||||
<Input v-model="form.userId" placeholder="UserBase 用户主键" />
|
<UserSelectInput
|
||||||
</FormItem>
|
:model-value="form.userId"
|
||||||
<FormItem label="姓名快照">
|
:display-name="form.userName"
|
||||||
<Input v-model="form.userName" placeholder="可留空,点检索自动补全" style="width: 180px" />
|
@update:model-value="form.userId = $event"
|
||||||
<Button :loading="lookingUp" @click="onLookup" style="margin-left: 8px">检索</Button>
|
@update:display-name="form.userName = $event"
|
||||||
|
@on-change="onUserPick"
|
||||||
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 审批节点配置抽屉(方案 §3.4):打开时把节点值拷进本地表单,确定时写回——
|
// 审批节点配置抽屉(方案 §3.4):打开时把节点值拷进本地表单,确定时写回——
|
||||||
// 分段控件切换只改本地状态,不会因重渲染丢失未保存输入(原型阶段踩过该坑)。
|
// 分段控件切换只改本地状态,不会因重渲染丢失未保存输入(原型阶段踩过该坑)。
|
||||||
|
// 指定用户改为树形选人弹窗(参照 Notice 自定义组选人),id+name 同帧拿到,不再手输 ID。
|
||||||
import { computed, inject, reactive, ref, watch } from "vue";
|
import { computed, inject, reactive, ref, watch } from "vue";
|
||||||
import { Message } from "view-ui-plus";
|
import { Message } from "view-ui-plus";
|
||||||
|
import UserCheckModal from "@/components/UserCheckModal.vue";
|
||||||
import { flowApi } from "@/api/module/flow";
|
import { flowApi } from "@/api/module/flow";
|
||||||
import { ApprovalNode, EDITOR_CTX } from "../model";
|
import { ApprovalNode, EDITOR_CTX } from "../model";
|
||||||
|
|
||||||
@@ -28,8 +30,13 @@ const form = reactive({
|
|||||||
deadlineHours: null as number | null,
|
deadlineHours: null as number | null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lookupId = ref<number | null>(null);
|
const userCheckModal = ref<any>();
|
||||||
const lookingUp = ref(false);
|
const userModalSources = [
|
||||||
|
{
|
||||||
|
name: "基础平台",
|
||||||
|
loader: () => flowApi.adminUserTree().send() as Promise<any[]>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
function syncFromNode(): void {
|
function syncFromNode(): void {
|
||||||
const n = props.node;
|
const n = props.node;
|
||||||
@@ -40,7 +47,6 @@ function syncFromNode(): void {
|
|||||||
form.userIds = n.assignee?.type === "USERS" ? [...n.assignee.userIds] : [];
|
form.userIds = n.assignee?.type === "USERS" ? [...n.assignee.userIds] : [];
|
||||||
form.countersign = n.countersign;
|
form.countersign = n.countersign;
|
||||||
form.deadlineHours = n.deadlineHours;
|
form.deadlineHours = n.deadlineHours;
|
||||||
lookupId.value = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -76,24 +82,19 @@ function userNameOf(id: number): string {
|
|||||||
return ctx.userNames[id] || `#${id}`;
|
return ctx.userNames[id] || `#${id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onLookup(): Promise<void> {
|
/** 选人弹窗提交:选中后的 id+name 合入本地 userIds 与姓名快照(不再逐个 byId 检索) */
|
||||||
if (!lookupId.value) {
|
function onUsersPicked(arr: any[]): void {
|
||||||
Message.warning("请先填用户ID");
|
let added = 0;
|
||||||
return;
|
for (const u of arr || []) {
|
||||||
|
if (u?.id == null) continue;
|
||||||
|
const id = Number(u.id);
|
||||||
|
if (!form.userIds.includes(id)) {
|
||||||
|
form.userIds.push(id);
|
||||||
|
added++;
|
||||||
}
|
}
|
||||||
const id = Number(lookupId.value);
|
if (u.name) ctx.userNames[id] = u.name;
|
||||||
lookingUp.value = true;
|
|
||||||
try {
|
|
||||||
const u: any = await flowApi.mapLookupUser(id).send();
|
|
||||||
ctx.userNames[id] = u?.name || `#${id}`;
|
|
||||||
if (!form.userIds.includes(id)) form.userIds.push(id);
|
|
||||||
lookupId.value = null;
|
|
||||||
Message.success(`已添加:${u?.name || `#${id}`}`);
|
|
||||||
} catch {
|
|
||||||
Message.error("UserBase 检索失败");
|
|
||||||
} finally {
|
|
||||||
lookingUp.value = false;
|
|
||||||
}
|
}
|
||||||
|
Message.success(`已选择 ${added} 名用户`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeUser(id: number): void {
|
function removeUser(id: number): void {
|
||||||
@@ -162,8 +163,7 @@ function onRemove(): void {
|
|||||||
<div class="fld">
|
<div class="fld">
|
||||||
<label>指定用户</label>
|
<label>指定用户</label>
|
||||||
<div class="user-add">
|
<div class="user-add">
|
||||||
<InputNumber v-model="lookupId" :min="1" placeholder="填 UserBase 用户ID" style="flex: 1" />
|
<Button type="primary" @click="userCheckModal?.show()">选择用户</Button>
|
||||||
<Button type="primary" :loading="lookingUp" @click="onLookup">检索并添加</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="user-tags">
|
<div class="user-tags">
|
||||||
<Tag
|
<Tag
|
||||||
@@ -208,6 +208,13 @@ function onRemove(): void {
|
|||||||
<Button type="primary" @click="onOk">确定</Button>
|
<Button type="primary" @click="onOk">确定</Button>
|
||||||
</div>
|
</div>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
|
<UserCheckModal
|
||||||
|
ref="userCheckModal"
|
||||||
|
:sources="userModalSources"
|
||||||
|
:userids="form.userIds"
|
||||||
|
@on-submit="onUsersPicked"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user