queryForm.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. <template>
  2. <div class="form-filter">
  3. <el-form label-position="left" :model="queryForm" class='form-content'>
  4. <template v-for="(option, index) in option.queryFormFields" :key="index">
  5. <!-- input -->
  6. <el-form-item :label="option.label" class='form-input r-24 border position-box'
  7. v-if="option.inputType == 'input'" @mouseenter="option.isShowDelIcon = true"
  8. @mouseleave="option.isShowDelIcon = false">
  9. <el-input class="input-deep input-w" v-model="queryForm.data[option.key]" :placeholder="`请输入${option.label}`"
  10. clearable>
  11. </el-input>
  12. <template v-if='!option.defaultFlag'>
  13. <view class='icon-del' @click='del(option)' v-show='option.isShowDelIcon'>
  14. <el-icon>
  15. <CircleCloseFilled color='#ccc' :size='30' />
  16. </el-icon>
  17. </view>
  18. </template>
  19. </el-form-item>
  20. <!-- select -->
  21. <el-form-item :label="option.label" class='form-select-single r-24 border position-box'
  22. v-if="option.inputType == 'select-single'" @mouseenter="option.isShowDelIcon = true"
  23. @mouseleave="option.isShowDelIcon = false">
  24. <el-select class='min-w ' v-model="queryForm.data[option.key]" placeholder=""
  25. @change='singleChange($event,option)'>
  26. <el-option v-for="item in option.listOption" :key="item.value" :label="item.label" :value="item.value">
  27. </el-option>
  28. </el-select>
  29. <template v-if='!option.defaultFlag'>
  30. <view class='icon-del' @click='del(option)' v-show='option.isShowDelIcon'>
  31. <el-icon>
  32. <CircleCloseFilled color='#ccc' :size='30' />
  33. </el-icon>
  34. </view>
  35. </template>
  36. </el-form-item>
  37. <!-- select 多选-->
  38. <el-form-item :label="option.label" class='form-select-multiple r-24 border position-box'
  39. v-if="option.inputType == 'select-multiple'" @mouseenter="option.isShowDelIcon = true"
  40. @mouseleave="option.isShowDelIcon = false">
  41. <el-select class='min-w' multiple v-model="queryForm.data[option.key]" value-key="value" placeholder=""
  42. @change='moreChange($event,option,option.listOption)'>
  43. <el-option v-for="item in option.listOption" :key="item.value" :label="item.label" :value="item.value">
  44. </el-option>
  45. <template #tag>
  46. <view v-show='option.selectValue'>
  47. <text v-if='option.listOption'>{{option.selectValue}}</text>
  48. <text class='text-color' v-else>请选择</text>
  49. </view>
  50. </template>
  51. </el-select>
  52. <template v-if='!option.defaultFlag'>
  53. <view class='icon-del' @click='del(option)' v-show='option.isShowDelIcon'>
  54. <el-icon>
  55. <CircleCloseFilled color='#ccc' :size='30' />
  56. </el-icon>
  57. </view>
  58. </template>
  59. </el-form-item>
  60. <!-- 日期 -->
  61. <el-form-item :label="option.label" class='form-date-picker r-24 border position-box'
  62. v-if="option.inputType == 'date-picker'" @mouseenter="option.isShowDelIcon = true"
  63. @mouseleave="option.isShowDelIcon = false">
  64. <el-date-picker v-model="queryForm.data[option.key]" type="daterange" unlink-panels start-placeholder="开始日期"
  65. end-placeholder="结束日期" :shortcuts="shortcuts" value-format="YYYY-MM-DD">
  66. <template #range-separator>
  67. <view>⇀</view>
  68. </template>
  69. </el-date-picker>
  70. <template v-if='!option.defaultFlag'>
  71. <view class='icon-del' @click='del(option)' v-show='option.isShowDelIcon'>
  72. <el-icon>
  73. <CircleCloseFilled color='#ccc' :size='30' />
  74. </el-icon>
  75. </view>
  76. </template>
  77. </el-form-item>
  78. <!-- 年份 -->
  79. <el-form-item :label="option.label" class='form-date-picker r-24 border position-box'
  80. v-if="option.inputType == 'date-year'" @mouseenter="option.isShowDelIcon = true"
  81. @mouseleave="option.isShowDelIcon = false">
  82. <el-date-picker v-model="queryForm.data[option.key]" type="year" placeholder="选择年份" value-format="YYYY">
  83. <template #range-separator>
  84. <view>⇀</view>
  85. </template>
  86. </el-date-picker>
  87. <template v-if='!option.defaultFlag'>
  88. <view class='icon-del' @click='del(option)' v-show='option.isShowDelIcon'>
  89. <el-icon>
  90. <CircleCloseFilled color='#ccc' :size='30' />
  91. </el-icon>
  92. </view>
  93. </template>
  94. </el-form-item>
  95. <!-- tree selectValue: [], //选中的Key存储的值 listOption原始数据 -->
  96. <el-form-item :label="option.label" class='form-date-tree r-24 border position-box'
  97. v-if="option.inputType == 'date-tree'" @mouseenter="option.isShowDelIcon = true"
  98. @mouseleave="option.isShowDelIcon = false">
  99. <el-tree-select v-model="queryForm.data[option.key]" :data="option.listOption" :render-after-expand="false"
  100. :props="defaultProps" node-key="id" show-checkbox multiple
  101. @check="(data,checked)=>handleCheckChange(data,checked,option,queryForm.data[option.key])">
  102. <template #tag>
  103. <view v-show='option.selectValue'>{{option.selectValue}}</view>
  104. </template>
  105. </el-tree-select>
  106. <template v-if='!option.defaultFlag'>
  107. <view class='icon-del' @click='del(option)' v-show='option.isShowDelIcon'>
  108. <el-icon>
  109. <CircleCloseFilled color='#ccc' :size='30' />
  110. </el-icon>
  111. </view>
  112. </template>
  113. </el-form-item>
  114. <!-- 级联选择 -->
  115. <el-form-item :label="option.label" class='form-date-cascader r-24 border position-box'
  116. v-if="option.inputType == 'date-cascader'" @mouseenter="option.isShowDelIcon = true"
  117. @mouseleave="option.isShowDelIcon = false">
  118. <el-cascader v-model="queryForm.data[option.key]" :options="option.listOption" clearable
  119. :props="optionProps" />
  120. <template v-if='!option.defaultFlag'>
  121. <view class='icon-del' @click='del(option)' v-show='option.isShowDelIcon'>
  122. <el-icon>
  123. <CircleCloseFilled color='#ccc' :size='30' />
  124. </el-icon>
  125. </view>
  126. </template>
  127. </el-form-item>
  128. </template>
  129. <el-button type="primary" @click='query'>查询</el-button>
  130. </el-form>
  131. </div>
  132. </template>
  133. <script setup lang="ts">
  134. import { useRouter } from "vue-router";
  135. import { timestampFormat } from "@/utils/dayjs";
  136. import api from "@/api";
  137. const emit = defineEmits(["query", "delSelect"]);
  138. const props = defineProps({
  139. option: {
  140. type: Object,
  141. default: () => {
  142. return {
  143. labelWidth: {}, // label宽度,
  144. };
  145. },
  146. },
  147. });
  148. const defaultProps = {
  149. children: "children",
  150. label: "name",
  151. };
  152. const optionProps = {
  153. value: "areaCode",
  154. label: "areaCname",
  155. children: "child",
  156. };
  157. const showDelete = ref(false);
  158. let queryForm = reactive({ data: {} });
  159. const shortcuts = [
  160. {
  161. text: "今日",
  162. value: () => {
  163. const end = new Date();
  164. const start = new Date();
  165. start.setTime(start.getTime());
  166. return [start, end];
  167. },
  168. },
  169. {
  170. text: "最近7天",
  171. value: () => {
  172. const end = new Date();
  173. const start = new Date();
  174. start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
  175. return [start, end];
  176. },
  177. },
  178. {
  179. text: "本月",
  180. value: () => {
  181. const year = new Date().getFullYear();
  182. const month = new Date().getMonth() + 1;
  183. const date = new Date(year, month, 0).getDate();
  184. const end = new Date(year, month - 1, date);
  185. const start = new Date(year, new Date().getMonth(), 1);
  186. return [start, end];
  187. },
  188. },
  189. {
  190. text: "本年",
  191. value: () => {
  192. const end = new Date(new Date().getFullYear(), 11, 31);
  193. const start = new Date(new Date().getFullYear(), 0, 1);
  194. return [start, end];
  195. },
  196. },
  197. ];
  198. //获取默认值
  199. const propsOption = props.option;
  200. const isEmptyValue = (value) => {
  201. return (
  202. value == null ||
  203. value == undefined ||
  204. (Array.isArray(value) && value.length === 0)
  205. );
  206. };
  207. const initDefaultSaveForm = () => {
  208. const defaultSaveForm = {};
  209. const { queryFormFields = [] } = propsOption;
  210. const currentTimestamp = Date.now();
  211. const formatTime = (timestamp, format = "") => {
  212. return timestampFormat(timestamp, format);
  213. };
  214. // 处理不同输入类型
  215. const typeHandlers = {
  216. "date-picker": (item, baseValue) => {
  217. if (isEmptyValue(baseValue)) {
  218. const defaultTime = formatTime(currentTimestamp);
  219. return [defaultTime, defaultTime];
  220. }
  221. return baseValue;
  222. },
  223. "date-year": () => {
  224. return [formatTime(currentTimestamp, "YYYY")];
  225. },
  226. "select-multiple": (item, baseValue) => {
  227. const value = item.listOption ? baseValue : "";
  228. return value === "0" ? ["0"] : value;
  229. },
  230. "select-single": (item, baseValue) => {
  231. const value = item.listOption ? baseValue : "";
  232. if (item.isFirst) {
  233. singleChange(baseValue, item);
  234. }
  235. return value;
  236. },
  237. default: (_, baseValue) => baseValue,
  238. };
  239. queryFormFields.forEach((item) => {
  240. if (!item?.key) return;
  241. const { key, inputType } = item;
  242. const baseValue = queryForm.data[key] ?? item.defaultValue;
  243. const handler = typeHandlers[inputType] || typeHandlers.default;
  244. defaultSaveForm[key] = handler(item, baseValue);
  245. });
  246. // 更新表单数据并返回
  247. queryForm.data = { ...defaultSaveForm };
  248. return queryForm.data;
  249. };
  250. //多选中发生变化
  251. const moreChange = (selectedValues: CheckboxValueType, item, foodList) => {
  252. const foodLabels = getMultiLabels(selectedValues, foodList, "value");
  253. item.selectValue = foodLabels.join(",");
  254. };
  255. //tree change
  256. const handleCheckChange = (data, row, option, ids) => {
  257. const foodLabels = getMultiLabels(ids, row.checkedNodes, "id");
  258. option.selectValue = foodLabels.join(",");
  259. };
  260. //数据过滤
  261. const getMultiLabels = (selectedValues, foodList, key) => {
  262. return selectedValues.map((targetValue) => {
  263. const matchedItem = foodList.find((item) => item[key] === targetValue);
  264. return matchedItem ? matchedItem.label || matchedItem.name : "";
  265. });
  266. };
  267. //单选查联动数据
  268. const singleChange = async (val, option) => {
  269. if (option.isFirst) {
  270. //查询合作的保险公司数据
  271. const { data } = await api.manageApi.getCompanyListByCompanyId({
  272. companyId: val,
  273. });
  274. propsOption.queryFormFields.forEach((e) => {
  275. if (e.key == "partnerCompanyIds") {
  276. e.listOption = data;
  277. e.listOption.unshift({
  278. value: "0",
  279. label: "全部",
  280. });
  281. e.selectValue = "全部";
  282. queryForm.data.partnerCompanyIds = ["0"];
  283. }
  284. });
  285. }
  286. };
  287. //查询
  288. const query = () => {
  289. const from = { ...queryForm.data };
  290. const { queryFormFields } = propsOption;
  291. const typeHandlers = {
  292. "select-single": (item, value) => {
  293. // 特殊处理companyId,其他单选转换为数组格式
  294. return item.key === "companyId" ? value : [value || "0"];
  295. },
  296. "date-tree": (item, value) => {
  297. // 日期树选择器:无默认值时转换为空数组
  298. return item.selectValue ? value : [];
  299. },
  300. "select-multiple": (item, value) => {
  301. // 多选框:无默认值时转换为空数组
  302. return item.defaultValue ? value : [];
  303. },
  304. "date-picker": (item, value) => {
  305. // 日期选择器:无值时转换为空数组
  306. return value ? value : [];
  307. },
  308. "date-year": (item, value) => {
  309. // 年份选择器:确保始终返回数组格式
  310. if (!value) return [];
  311. return Array.isArray(value) ? value : [value];
  312. },
  313. };
  314. queryFormFields.forEach((item) => {
  315. const { inputType, key } = item;
  316. const handler = typeHandlers[inputType];
  317. if (handler) {
  318. from[key] = handler(item, from[key]);
  319. }
  320. });
  321. emit("query", from);
  322. };
  323. //删除
  324. const del = (option) => {
  325. const { inputType, key, id } = option;
  326. const isInputOrCompanyId = inputType === "input" || key === "companyId";
  327. if (isInputOrCompanyId) {
  328. queryForm.data[key] = "";
  329. } else {
  330. option.selectValue = "";
  331. queryForm.data[key] = [];
  332. }
  333. emit("delSelect", id);
  334. };
  335. // 暴露给父组件的方法
  336. defineExpose({
  337. initDefaultSaveForm,
  338. });
  339. </script>
  340. <style scoped lang="scss">
  341. .form-content {
  342. display: flex;
  343. flex-wrap: wrap;
  344. .r-24 {
  345. margin-right: 24px;
  346. }
  347. .border {
  348. border-radius: 2px 2px 2px 2px;
  349. border: 1px solid #e5e5e5;
  350. }
  351. :deep(.el-form-item__label) {
  352. padding-left: 12px;
  353. }
  354. .min-w {
  355. min-width: 200px;
  356. }
  357. .input-w {
  358. width: 200px;
  359. }
  360. .position-box {
  361. position: relative;
  362. .icon-del {
  363. position: absolute;
  364. right: -8px;
  365. top: -14px;
  366. }
  367. :deep(.el-icon) {
  368. font-size: 20px;
  369. }
  370. }
  371. .text-color {
  372. color: #a3a8b3;
  373. }
  374. }
  375. // input
  376. .form-input {
  377. .input-deep {
  378. :deep(.el-input__wrapper) {
  379. box-shadow: 0 0 0 0px var(--el-input-border-color, var(--el-border-color))
  380. inset;
  381. cursor: default;
  382. .el-input__inner {
  383. cursor: default !important;
  384. }
  385. }
  386. }
  387. }
  388. //select
  389. .form-select-single,
  390. .form-select-multiple {
  391. :deep(.el-select__wrapper) {
  392. border: none; /* 去除边框 */
  393. box-shadow: none;
  394. }
  395. }
  396. //picker
  397. .form-date-picker {
  398. :deep(.el-date-editor.el-input__wrapper) {
  399. border: none; /* 去除边框 */
  400. box-shadow: none;
  401. padding-left: 0;
  402. padding-right: 0;
  403. width: 240px;
  404. }
  405. :deep(.el-input__wrapper) {
  406. border: none; /* 去除边框 */
  407. box-shadow: none;
  408. }
  409. :deep(.el-date-editor .el-range__icon) {
  410. display: none;
  411. }
  412. }
  413. //tree
  414. .form-date-tree {
  415. :deep(.el-select__wrapper) {
  416. box-shadow: none;
  417. min-width: 240px;
  418. }
  419. }
  420. //el-cascader
  421. .form-date-cascader {
  422. :deep(.el-cascader .el-input__wrapper) {
  423. box-shadow: 0 0 0 0px var(--el-input-border-color, var(--el-border-color))
  424. inset;
  425. }
  426. }
  427. </style>