index.vue 16 KB

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