BasicInfo.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. <template>
  2. <el-form ref="formRef" :model="modelData" :rules="rules" label-width="120px" class="mt-20px">
  3. <el-form-item label="流程标识" prop="key" class="mb-20px">
  4. <div class="flex items-center">
  5. <el-input
  6. class="!w-440px"
  7. v-model="modelData.key"
  8. :disabled="!!modelData.id"
  9. placeholder="请输入流程标识,以字母或下划线开头"
  10. />
  11. <el-tooltip
  12. class="item"
  13. :content="modelData.id ? '流程标识不可修改!' : '新建后,流程标识不可修改!'"
  14. effect="light"
  15. placement="top"
  16. >
  17. <Icon icon="ep:question-filled" class="ml-5px" />
  18. </el-tooltip>
  19. </div>
  20. </el-form-item>
  21. <el-form-item label="流程名称" prop="name" class="mb-20px">
  22. <el-input
  23. v-model="modelData.name"
  24. :disabled="!!modelData.id"
  25. clearable
  26. placeholder="请输入流程名称"
  27. />
  28. </el-form-item>
  29. <el-form-item label="流程分类" prop="category" class="mb-20px">
  30. <el-select
  31. class="!w-full"
  32. v-model="modelData.category"
  33. clearable
  34. placeholder="请选择流程分类"
  35. >
  36. <el-option
  37. v-for="category in categoryList"
  38. :key="category.code"
  39. :label="category.name"
  40. :value="category.code"
  41. />
  42. </el-select>
  43. </el-form-item>
  44. <el-form-item label="流程图标" class="mb-20px">
  45. <UploadImg v-model="modelData.icon" :limit="1" height="64px" width="64px" />
  46. </el-form-item>
  47. <el-form-item label="流程描述" prop="description" class="mb-20px">
  48. <el-input v-model="modelData.description" clearable type="textarea" />
  49. </el-form-item>
  50. <el-form-item label="流程类型" prop="type" class="mb-20px">
  51. <el-radio-group v-model="modelData.type">
  52. <el-radio
  53. v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_TYPE)"
  54. :key="dict.value"
  55. :value="dict.value"
  56. >
  57. {{ dict.label }}
  58. </el-radio>
  59. </el-radio-group>
  60. </el-form-item>
  61. <el-form-item label="是否可见" prop="visible" class="mb-20px">
  62. <el-radio-group v-model="modelData.visible">
  63. <el-radio
  64. v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  65. :key="dict.value as string"
  66. :value="dict.value"
  67. >
  68. {{ dict.label }}
  69. </el-radio>
  70. </el-radio-group>
  71. </el-form-item>
  72. <el-form-item label="谁可以发起" prop="startUserType" class="mb-20px">
  73. <el-select
  74. v-model="modelData.startUserType"
  75. placeholder="请选择谁可以发起"
  76. @change="handleStartUserTypeChange"
  77. >
  78. <el-option label="全员" :value="0" />
  79. <el-option label="指定人员" :value="1" />
  80. <el-option label="指定部门" :value="2" />
  81. </el-select>
  82. <div v-if="modelData.startUserType === 1" class="mt-2 flex flex-wrap gap-2">
  83. <div
  84. v-for="user in selectedStartUsers"
  85. :key="user.id"
  86. class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
  87. >
  88. <el-avatar class="!m-5px" :size="28" v-if="user.avatar" :src="user.avatar" />
  89. <el-avatar class="!m-5px" :size="28" v-else>
  90. {{ user.nickname.substring(0, 1) }}
  91. </el-avatar>
  92. {{ user.nickname }}
  93. <Icon
  94. icon="ep:close"
  95. class="ml-2 cursor-pointer hover:text-red-500"
  96. @click="handleRemoveStartUser(user)"
  97. />
  98. </div>
  99. <el-button type="primary" link @click="openStartUserSelect">
  100. <Icon icon="ep:plus" /> 选择人员
  101. </el-button>
  102. </div>
  103. <div v-if="modelData.startUserType === 2" class="mt-2 flex flex-wrap gap-2">
  104. <div
  105. v-for="dept in selectedStartDepts"
  106. :key="dept.id"
  107. class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
  108. >
  109. <Icon icon="ep:office-building" class="!m-5px text-20px" />
  110. {{ dept.name }}
  111. <Icon
  112. icon="ep:close"
  113. class="ml-2 cursor-pointer hover:text-red-500"
  114. @click="handleRemoveStartDept(dept)"
  115. />
  116. </div>
  117. <el-button type="primary" link @click="openStartDeptSelect">
  118. <Icon icon="ep:plus" /> 选择部门
  119. </el-button>
  120. </div>
  121. </el-form-item>
  122. <el-form-item label="流程管理员" prop="managerUserIds" class="mb-20px">
  123. <div class="flex flex-wrap gap-2">
  124. <div
  125. v-for="user in selectedManagerUsers"
  126. :key="user.id"
  127. class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
  128. >
  129. <el-avatar class="!m-5px" :size="28" v-if="user.avatar" :src="user.avatar" />
  130. <el-avatar class="!m-5px" :size="28" v-else>
  131. {{ user.nickname.substring(0, 1) }}
  132. </el-avatar>
  133. {{ user.nickname }}
  134. <Icon
  135. icon="ep:close"
  136. class="ml-2 cursor-pointer hover:text-red-500"
  137. @click="handleRemoveManagerUser(user)"
  138. />
  139. </div>
  140. <el-button type="primary" link @click="openManagerUserSelect">
  141. <Icon icon="ep:plus" />选择人员
  142. </el-button>
  143. </div>
  144. </el-form-item>
  145. </el-form>
  146. <!-- 用户选择弹窗 -->
  147. <UserSelectForm ref="userSelectFormRef" @confirm="handleUserSelectConfirm" />
  148. <!-- 部门选择弹窗 -->
  149. <DeptSelectForm
  150. ref="deptSelectFormRef"
  151. :multiple="true"
  152. :check-strictly="true"
  153. @confirm="handleDeptSelectConfirm"
  154. />
  155. </template>
  156. <script lang="ts" setup>
  157. import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '@/utils/dict'
  158. import { UserVO } from '@/api/system/user'
  159. import { DeptVO } from '@/api/system/dept'
  160. import { CategoryVO } from '@/api/bpm/category'
  161. const props = defineProps({
  162. categoryList: {
  163. type: Array as PropType<CategoryVO[]>,
  164. required: true
  165. },
  166. userList: {
  167. type: Array,
  168. required: true
  169. },
  170. deptList: {
  171. type: Array,
  172. required: true
  173. }
  174. })
  175. const formRef = ref()
  176. const selectedStartUsers = ref<UserVO[]>([])
  177. const selectedStartDepts = ref<DeptVO[]>([])
  178. const selectedManagerUsers = ref<UserVO[]>([])
  179. const userSelectFormRef = ref()
  180. const deptSelectFormRef = ref()
  181. const currentSelectType = ref<'start' | 'manager'>('start')
  182. const rules = {
  183. name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }],
  184. key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
  185. category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }],
  186. type: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
  187. visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
  188. managerUserIds: [{ required: true, message: '流程管理员不能为空', trigger: 'blur' }]
  189. }
  190. // 创建本地数据副本
  191. const modelData = defineModel<any>()
  192. // 初始化选中的用户
  193. watch(
  194. () => modelData.value,
  195. (newVal) => {
  196. if (newVal.startUserIds?.length) {
  197. selectedStartUsers.value = props.userList.filter((user: UserVO) =>
  198. newVal.startUserIds.includes(user.id)
  199. ) as UserVO[]
  200. } else {
  201. selectedStartUsers.value = []
  202. }
  203. if (newVal.startDeptIds?.length) {
  204. selectedStartDepts.value = props.deptList.filter((dept: DeptVO) =>
  205. newVal.startDeptIds.includes(dept.id)
  206. ) as DeptVO[]
  207. } else {
  208. selectedStartDepts.value = []
  209. }
  210. if (newVal.managerUserIds?.length) {
  211. selectedManagerUsers.value = props.userList.filter((user: UserVO) =>
  212. newVal.managerUserIds.includes(user.id)
  213. ) as UserVO[]
  214. } else {
  215. selectedManagerUsers.value = []
  216. }
  217. },
  218. {
  219. immediate: true
  220. }
  221. )
  222. /** 打开发起人选择 */
  223. const openStartUserSelect = () => {
  224. currentSelectType.value = 'start'
  225. userSelectFormRef.value.open(0, selectedStartUsers.value)
  226. }
  227. /** 打开部门选择 */
  228. const openStartDeptSelect = () => {
  229. deptSelectFormRef.value.open(selectedStartDepts.value)
  230. }
  231. /** 打开管理员选择 */
  232. const openManagerUserSelect = () => {
  233. currentSelectType.value = 'manager'
  234. userSelectFormRef.value.open(0, selectedManagerUsers.value)
  235. }
  236. /** 处理用户选择确认 */
  237. const handleUserSelectConfirm = (_, users: UserVO[]) => {
  238. if (currentSelectType.value === 'start') {
  239. modelData.value = {
  240. ...modelData.value,
  241. startUserIds: users.map((u) => u.id)
  242. }
  243. } else {
  244. modelData.value = {
  245. ...modelData.value,
  246. managerUserIds: users.map((u) => u.id)
  247. }
  248. }
  249. }
  250. /** 处理部门选择确认 */
  251. const handleDeptSelectConfirm = (depts: DeptVO[]) => {
  252. modelData.value = {
  253. ...modelData.value,
  254. startDeptIds: depts.map((d) => d.id)
  255. }
  256. }
  257. /** 处理发起人类型变化 */
  258. const handleStartUserTypeChange = (value: number) => {
  259. if (value === 0) {
  260. modelData.value = {
  261. ...modelData.value,
  262. startUserIds: [],
  263. startDeptIds: []
  264. }
  265. } else if (value === 1) {
  266. modelData.value = {
  267. ...modelData.value,
  268. startDeptIds: []
  269. }
  270. } else if (value === 2) {
  271. modelData.value = {
  272. ...modelData.value,
  273. startUserIds: []
  274. }
  275. }
  276. }
  277. /** 移除发起人 */
  278. const handleRemoveStartUser = (user: UserVO) => {
  279. modelData.value = {
  280. ...modelData.value,
  281. startUserIds: modelData.value.startUserIds.filter((id: number) => id !== user.id)
  282. }
  283. }
  284. /** 移除部门 */
  285. const handleRemoveStartDept = (dept: DeptVO) => {
  286. modelData.value = {
  287. ...modelData.value,
  288. startDeptIds: modelData.value.startDeptIds.filter((id: number) => id !== dept.id)
  289. }
  290. }
  291. /** 移除管理员 */
  292. const handleRemoveManagerUser = (user: UserVO) => {
  293. modelData.value = {
  294. ...modelData.value,
  295. managerUserIds: modelData.value.managerUserIds.filter((id: number) => id !== user.id)
  296. }
  297. }
  298. /** 表单校验 */
  299. const validate = async () => {
  300. await formRef.value?.validate()
  301. }
  302. defineExpose({
  303. validate
  304. })
  305. </script>
  306. <style lang="scss" scoped>
  307. .bg-gray-100 {
  308. background-color: #f5f7fa;
  309. transition: all 0.3s;
  310. &:hover {
  311. background-color: #e6e8eb;
  312. }
  313. .ep-close {
  314. font-size: 14px;
  315. color: #909399;
  316. transition: color 0.3s;
  317. &:hover {
  318. color: #f56c6c;
  319. }
  320. }
  321. }
  322. </style>