index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <template>
  2. <el-dialog v-model="props.visible" width="800px" :show-close="false" :close-on-click-modal="false" top="10vh">
  3. <template #header="{ titleId, titleClass }">
  4. <div class="flex justify-between items-center border-b pb-3">
  5. <h4 :id="titleId" :class="titleClass">{{ title || "新建任务" }}</h4>
  6. <div>
  7. <el-button v-if="!editForm" type="primary" :loading="['2'].includes(props.saveLoading)"
  8. @click="submitForm(formRef, true)">保存并新建</el-button>
  9. <el-button type="primary" :loading="['2'].includes(props.saveLoading)"
  10. @click="submitForm(formRef, false)">保存</el-button>
  11. <el-button @click="closeVisible()">取消</el-button>
  12. </div>
  13. </div>
  14. </template>
  15. <div class="h-[55vh] overflow-y-auto scroll-bar mt-5">
  16. <el-form ref="formRef" :model="form" label-width="7em" :rules="rules" class="flex flex-wrap form">
  17. <el-form-item label="任务名称:" prop="taskName" required>
  18. <el-input v-model="form.taskName" type="textarea" placeholder="请输入任务名称" clearable maxlength="100"
  19. show-word-limit :disabled="disabledList && disabledList.includes('taskName')" />
  20. </el-form-item>
  21. <el-form-item prop="priority" label="优先级:" required>
  22. <el-select v-model="form.priority" placeholder="请选择" clearable
  23. :disabled="disabledList && disabledList.includes('priority')">
  24. <el-option v-for="item in PRIORITY " :key="item.value" :value="item.value" :label="item.label" />
  25. </el-select>
  26. </el-form-item>
  27. <el-form-item :label="form.taskType">
  28. <template #label>
  29. <el-select v-model="form.taskType" class="border resetSelect" style="width: 100px" @change="changeTaskType"
  30. :disabled="disabledList && disabledList.includes('taskType')">
  31. <el-option v-for="item in TASK_TYPE" :key="item.value" :value="item.value" :label="item.label" />
  32. </el-select>
  33. </template>
  34. <template v-for="item in TASK_TYPE_FIELD">
  35. <el-select v-model="form[item.field]" v-if="form.taskType == item.type" placeholder="请选择" clearable
  36. filterable :disabled="disabledList && disabledList.includes(item.field)" @change="(e: any) => {taskFormChange(e, item.field)}">
  37. <el-option v-for="v in taskTypeValueData" :key="v.id" :value="v[item.valueIndex]"
  38. :label="v[item.labelIndex]" />
  39. </el-select>
  40. </template>
  41. </el-form-item>
  42. <el-form-item label="联系人:" v-if="TASK_TYPE.find(v => v.value == (form.taskType || '1'))?.show">
  43. <el-select v-model="form.contactsId" placeholder="请选择" clearable filterable
  44. :disabled="(disabledList && disabledList.includes('contactsId')) || !form[TASK_TYPE_FIELD[form.taskType].field]">
  45. <el-option v-for="item in contactValueData" :key="item.id" :value="item.id" :label="item.name" />
  46. </el-select>
  47. </el-form-item>
  48. <el-form-item label="执行人:">
  49. <!-- <el-select v-model="form.executorId" placeholder="请选择" clearable multiple filterable
  50. :disabled="disabledList && disabledList.includes('executorId')">
  51. <el-option v-for="item in executorValueData" :key="item.id" :value="item.id" :label="item.name" />
  52. </el-select> -->
  53. <personnel-search v-model="form.executorId" :size="''" multiple placeholder="请选择" :disabled="disabledList && disabledList.includes('executorId')"></personnel-search>
  54. </el-form-item>
  55. <el-form-item label="重复提醒:">
  56. <el-switch v-model="form.isRepeat" :active-value="1" :inactive-value="0" @change="changeRepeat" />
  57. </el-form-item>
  58. <template v-if="form.isRepeat === 1">
  59. <el-form-item label="重复类型:">
  60. <el-select v-model="form.repeatType" placeholder="请选择" @change="changeRepeatType">
  61. <el-option v-for="item in REPEAT_TYPE" :key="item.value" :value="item.value" :label="item.label" />
  62. </el-select>
  63. </el-form-item>
  64. <template v-if="[0, 1, 2, 3].includes(form.repeatType)">
  65. <el-form-item label="每:" v-if="form.repeatType == 3">
  66. <el-input-number v-model="form.repeatDesignSameday" :step="1" :precision="0" controls-position="right" :min="1" />天
  67. </el-form-item>
  68. <el-form-item label="结束:">
  69. <el-radio-group v-model="form.endType" @change="changeEndType">
  70. <el-radio :label="1" class="w-full">永不</el-radio>
  71. <el-radio :label="2" class="w-full mb-3"><el-input-number v-model="form.repeatEndCount" :precision="0" :step="1" :min="1"
  72. controls-position="right" :disabled="form.endType != 2" />
  73. 次以后
  74. </el-radio>
  75. <el-radio :label="3" class="w-full">
  76. <el-date-picker v-model="form.repeatEndDate" type="date" placeholder="选择日期" style="width:65%"
  77. :disabled-date="(value: Date) => (new Date() > new Date(value))" value-format="YYYY-MM-DD"
  78. :disabled="form.endType != 3" />
  79. 以后
  80. </el-radio>
  81. </el-radio-group>
  82. </el-form-item>
  83. </template>
  84. <template v-if="[4].includes(form.repeatType)">
  85. <el-form-item v-for="(v, i) in customeDate" class="flex justify-between items-center customeDate">
  86. <div>
  87. 第{{ i + 1 }}次重复在
  88. <el-input-number :model-value="Number(v)" :step="1" :precision="0" controls-position="right" @change="changeDate(i, $event)"
  89. :min="1" />
  90. 天后
  91. </div>
  92. <div>
  93. <el-icon size="24" @click="deleteCustomeDateItem(i)" class=" hover:text-blue-500 cursor-pointer">
  94. <Delete />
  95. </el-icon>
  96. </div>
  97. </el-form-item>
  98. <el-form-item>
  99. <div>
  100. <el-icon size="24" @click="addCustomeDateItem()" class=" hover:text-blue-500 cursor-pointer">
  101. <Plus />
  102. </el-icon>
  103. </div>
  104. </el-form-item>
  105. </template>
  106. </template>
  107. <!-- <el-form-item label="开始时间:" class="w50">
  108. <el-date-picker v-model="form.startDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
  109. :disabled="disabledList && disabledList.includes('startDate')" />
  110. </el-form-item>
  111. <el-form-item label="截止时间:" class="w50">
  112. <el-date-picker v-model="form.endDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
  113. :disabled="disabledList && disabledList.includes('endDate')" />
  114. </el-form-item> -->
  115. <el-form-item label="开始时间:" class="w50">
  116. <el-date-picker v-model="form.startDate" type="datetime" placeholder="选择日期" value-format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
  117. :disabled="disabledList && disabledList.includes('startDate')" />
  118. </el-form-item>
  119. <el-form-item label="截止时间:" class="w50">
  120. <el-date-picker v-model="form.endDate" type="datetime" placeholder="选择日期" value-format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
  121. :disabled="disabledList && disabledList.includes('endDate')" />
  122. </el-form-item>
  123. </el-form>
  124. <GenerateForm ref="generateFormRef" :data="generateFormData" :value="form" />
  125. <div v-if="showLog">
  126. <el-form-item label="操作记录" label-width="7em">
  127. <div class="w-full">
  128. <div v-for="item in form.taskLogs" class=" border-b-2 w-full pl-3">
  129. <!-- {{ `${dayjs(item.modTime).format('YYYY-MM-DD HH:mm:ss')} ${item.userName} ${item.content}` }} -->
  130. {{ dayjs(item.modTime).format('YYYY-MM-DD HH:mm:ss') }}
  131. <TextTranslation translationTypes="userName" :translationValue="item.userName" />
  132. {{ item.content }}
  133. </div>
  134. </div>
  135. </el-form-item>
  136. </div>
  137. </div>
  138. </el-dialog>
  139. </template>
  140. <script lang="ts" setup>
  141. import { ref, watch } from 'vue';
  142. import { PRIORITY, TASK_TYPE, TASK_TYPE_FIELD, defalutModalForm, REPEAT_TYPE, CUSTOMER_FORM_URL, defaultGenerateFormData, ALL_CUSTOM, ALL_CLUE, ALL_BUSINESS, ALL_CONTACTS, ALL_ORDERS, ALL_USERS, getTaskType } from "./api";
  143. import { GenerateForm } from "@zmjs/form-design"
  144. import { get, post } from '@/utils/request';
  145. import { Delete, Plus } from "@element-plus/icons-vue"
  146. import { FormInstance, dayjs } from 'element-plus';
  147. import { getFromValue } from '@/utils/tools';
  148. import { Props, Emits } from './type';
  149. import { URL_GETALL } from '@/pages/contacts/api';
  150. import personnelSearch from '@/components/translationComponent/personnelSearch/personnelSearch.vue';
  151. const props = defineProps<Props>()
  152. const emits = defineEmits<Emits>();
  153. watch(() => props.saveLoading, (val) => {
  154. if (val == "3") {
  155. formRef.value?.resetFields();
  156. form.value = { ...defalutModalForm };
  157. generateFormRef.value?.reset();
  158. taskTypeValueData.value = customeData.value
  159. }
  160. })
  161. watch(() => props.visible, (val) => {
  162. if (val) {
  163. get(CUSTOMER_FORM_URL).then(res => {
  164. if (Array.isArray(res.data) && res.data.length > 0) {
  165. generateFormData.value = JSON.parse(res.data[0].config)
  166. }
  167. })
  168. }
  169. })
  170. const customeData = ref<any>([])
  171. const businessData = ref<any>([])
  172. const orderData = ref<any>([])
  173. const clueData = ref<any>([])
  174. watch(() => props.editForm, (val) => {
  175. let taskType = 0;
  176. if (val) {
  177. taskType = getTaskType(val);
  178. }
  179. get(ALL_CUSTOM, {}).then(({ data }) => {
  180. customeData.value = data;//客户
  181. if (taskType == 0) {
  182. taskTypeValueData.value = data;
  183. }
  184. })
  185. get(ALL_BUSINESS, {}).then(({ data }) => {
  186. businessData.value = data;//商机
  187. if (taskType == 1) {
  188. taskTypeValueData.value = data;
  189. }
  190. })
  191. post(ALL_ORDERS, { pageIndex: -1, pageSize: -1 }).then(({ data }) => {
  192. orderData.value = data.record;//销售订单
  193. if (taskType == 2) {
  194. taskTypeValueData.value = data.record;
  195. }
  196. })
  197. get(ALL_CLUE, {}).then(({ data }) => {
  198. clueData.value = data;//线索
  199. if (taskType == 3) {
  200. taskTypeValueData.value = data;
  201. }
  202. })
  203. get(ALL_CONTACTS, {}).then(({ data }) => {
  204. contactValueData.value = data;//联系人
  205. })
  206. get(ALL_USERS, {}).then(({ data }) => {
  207. executorValueData.value = data;
  208. })
  209. if (val) {
  210. form.value = {
  211. ...val,
  212. taskType,
  213. }
  214. } else {
  215. form.value = { ...defalutModalForm }
  216. }
  217. })
  218. const rules = ref({
  219. taskName: [
  220. { required: true, message: '请输入任务名称', trigger: 'blur' }
  221. ],
  222. priority: [
  223. { required: true, message: '请选择优先级', trigger: 'change' }
  224. ]
  225. })
  226. const form = ref<any>({});
  227. const formRef = ref<FormInstance>();
  228. const generateFormRef = ref<InstanceType<typeof GenerateForm>>();
  229. const generateFormData = ref<any>({ ...defaultGenerateFormData });
  230. function disableMinute() {
  231. // 只允许选择 00 和 30 分钟
  232. const allowed = [0, 30];
  233. const disabled = [];
  234. for (let i = 0; i < 60; i++) {
  235. if (!allowed.includes(i)) {
  236. disabled.push(i);
  237. }
  238. }
  239. // return disabled;
  240. return disabled;
  241. }
  242. function taskFormChange(e: any, field: 'customId' | 'businessOpportunityId' | 'orderId' | 'clueId') {
  243. const fieldMap = {
  244. 'customId': 'customerId',
  245. 'businessOpportunityId': 'businessId',
  246. 'orderId': 'salesId',
  247. 'clueId': ''
  248. };
  249. let fieldStr = fieldMap[field] || '';
  250. updateContactPerson(e, fieldStr)
  251. }
  252. function updateContactPerson(val: any, field: string) {
  253. let formVal = field ? { [field]: val } : {}
  254. get(URL_GETALL, { ...formVal }).then(({ data }) => {
  255. contactValueData.value = data;//联系人
  256. })
  257. }
  258. function closeVisible() {
  259. formRef.value?.resetFields();
  260. generateFormData.value = { ...defaultGenerateFormData };
  261. generateFormRef.value?.reset()
  262. emits('close')
  263. }
  264. function submitForm(formEl: FormInstance | undefined, isClose: boolean) {
  265. if (!formEl) return
  266. formEl.validate((valid: boolean) => {
  267. if (!valid) {
  268. return false as any
  269. }
  270. let numList = []
  271. if(customeDate.value) {
  272. numList = customeDate.value.filter((item: any) => item)
  273. }
  274. const repeatDesignDay = numList.join(",")
  275. generateFormRef.value?.getData().then((res: any) => {
  276. console.log(`{
  277. ...form.value,
  278. repeatDesignDay,
  279. ...res
  280. }`, {
  281. ...form.value,
  282. repeatDesignDay,
  283. ...res
  284. });
  285. let submitData = getFromValue({
  286. ...form.value,
  287. repeatDesignDay,
  288. ...res
  289. })
  290. emits('submit', submitData, isClose)
  291. }).catch((err: any) => {
  292. console.log(err);
  293. })
  294. })
  295. }
  296. const taskTypeValueData = ref<any>([])
  297. function changeTaskType(value: TASK_VALUE_TYPE) {
  298. form.value = {
  299. ...form.value,
  300. taskType: value,
  301. customId: null, // 客户id 0
  302. businessOpportunityId: null, //商机id 1
  303. orderId: null, // 订单id 2
  304. clueId: null, //线索id 3
  305. contactsId: null, //联系人id
  306. }
  307. switch (value) {
  308. case 0:
  309. taskTypeValueData.value = [];
  310. setTimeout(() => {
  311. taskTypeValueData.value = customeData.value;
  312. }, 500)
  313. break;
  314. case 1:
  315. taskTypeValueData.value = [];
  316. setTimeout(() => {
  317. taskTypeValueData.value = businessData.value;
  318. }, 500)
  319. break;
  320. case 2:
  321. taskTypeValueData.value = [];
  322. setTimeout(() => {
  323. taskTypeValueData.value = orderData.value
  324. }, 500)
  325. break;
  326. case 3:
  327. taskTypeValueData.value = [];
  328. setTimeout(() => {
  329. taskTypeValueData.value = clueData.value;
  330. }, 500)
  331. break;
  332. default:
  333. const _n: never = value;
  334. break;
  335. }
  336. }
  337. const contactValueData = ref<any>([])
  338. const executorValueData = ref<any>([])
  339. function changeRepeat(value: string | number | boolean) {
  340. form.value = {
  341. ...form.value,
  342. isRepeat: value,
  343. repeatType: 0, //重复类型
  344. endType: 1, //结束类型
  345. repeatEndNever: 1,
  346. repeatEndCount: null, //重复指定次数次数后结束
  347. repeatEndDate: null, //重复到指定日期后结束
  348. repeatDesignDay: null, //自定义日期
  349. repeatDesignSameday: null, //自定义周期
  350. }
  351. customeDate.value = [];
  352. }
  353. function changeRepeatType(value: REPEAT_VALUE_TYPE) {
  354. form.value = {
  355. ...form.value,
  356. repeatType: value, //重复类型
  357. endType: 1, //结束类型
  358. repeatEndNever: 1,
  359. repeatEndCount: null, //重复指定次数次数后结束
  360. repeatEndDate: null, //重复到指定日期后结束
  361. repeatDesignDay: null, //自定义日期
  362. repeatDesignSameday: null, //自定义周期
  363. }
  364. customeDate.value = [];
  365. }
  366. function changeEndType(value: string | number | boolean | undefined) {
  367. form.value = {
  368. ...form.value,
  369. endType: value, //重复类型
  370. repeatEndNever: value == 1 ? 1 : null,//永不结束
  371. repeatEndCount: null, //重复指定次数次数后结束
  372. repeatEndDate: null, //重复到指定日期后结束
  373. repeatDesignDay: null, //自定义日期
  374. }
  375. }
  376. const customeDate = ref<any>([])
  377. function changeDate(index: number, value: number | undefined) {
  378. customeDate.value[index] = value;
  379. }
  380. function deleteCustomeDateItem(index: number) {
  381. customeDate.value = customeDate.value.filter((_item: number, i: number) => i !== index);
  382. }
  383. function addCustomeDateItem() {
  384. customeDate.value.push(undefined)
  385. }
  386. </script>
  387. <style lang="scss">
  388. .resetSelect {
  389. border: 0;
  390. .el-select__wrapper {
  391. box-shadow: none;
  392. padding-right: 0;
  393. }
  394. .el-select__selected-item {
  395. text-align: right;
  396. }
  397. }
  398. .customeDate {
  399. .el-form-item__content {
  400. justify-content: space-between;
  401. }
  402. }
  403. .el-form-item {
  404. width: 100%;
  405. }
  406. .form {
  407. .w50 {
  408. @apply w-1/2;
  409. }
  410. }
  411. </style>