search.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <template>
  2. <div class="table-search-container" v-if="props.search.length > 0">
  3. <el-form ref="tableSearchRef" :model="state.innerModelValue" label-width="100px" class="table-form">
  4. <el-row :gutter="20">
  5. <!-- <el-col :xs="12" :sm="8" :md="8" :lg="6" :xl="4" class="mb20"></el-col> -->
  6. <el-col :xs="12" :sm="5" :md="5" :lg="6" :xl="4" class="mb20" v-for="(val, key) in search" :key="key" v-show="key < 3 || state.isToggle">
  7. <template v-if="val.type">
  8. <el-form-item
  9. label-width="auto"
  10. :label="val.label"
  11. :prop="val.prop"
  12. :rules="[{ required: val.required, message: `${val.label}不能为空`, trigger: val.type === 'input' ? 'blur' : 'change' }]"
  13. >
  14. <el-input
  15. v-model="state.innerModelValue[val.prop]"
  16. v-bind="val.comProps"
  17. :placeholder="val.placeholder"
  18. :clearable="!val.required"
  19. v-if="val.type === 'input'"
  20. @keyup.enter="onSearch(tableSearchRef)"
  21. @change="val.change"
  22. class="w100"
  23. />
  24. <el-date-picker
  25. v-model="state.innerModelValue[val.prop]"
  26. v-bind="val.comProps"
  27. type="date"
  28. :placeholder="val.placeholder"
  29. :clearable="!val.required"
  30. v-else-if="val.type === 'date'"
  31. @change="val.change"
  32. class="w100"
  33. />
  34. <el-date-picker
  35. v-model="state.innerModelValue[val.prop]"
  36. v-bind="val.comProps"
  37. type="monthrange"
  38. value-format="YYYY/MM/DD"
  39. :placeholder="val.placeholder"
  40. :clearable="!val.required"
  41. v-else-if="val.type === 'monthrange'"
  42. @change="val.change"
  43. class="w100"
  44. />
  45. <el-date-picker
  46. v-model="state.innerModelValue[val.prop]"
  47. v-bind="val.comProps"
  48. type="daterange"
  49. value-format="YYYY/MM/DD"
  50. range-separator="至"
  51. start-placeholder="开始日期"
  52. end-placeholder="结束日期"
  53. :clearable="!val.required"
  54. :shortcuts="shortcuts"
  55. :default-time="defaultTime"
  56. v-else-if="val.type === 'daterange'"
  57. @change="val.change"
  58. class="w100"
  59. />
  60. <el-select
  61. v-model="state.innerModelValue[val.prop]"
  62. v-bind="val.comProps"
  63. :clearable="!val.required"
  64. :placeholder="val.placeholder"
  65. v-else-if="val.type === 'select'"
  66. @change="val.change"
  67. class="w100"
  68. >
  69. <el-option v-for="item in val.options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
  70. </el-select>
  71. <el-cascader
  72. v-else-if="val.type === 'cascader' && val.cascaderData"
  73. :options="val.cascaderData"
  74. :clearable="!val.required"
  75. filterable
  76. :props="val.cascaderProps ? val.cascaderProps : state.cascaderProps"
  77. :placeholder="val.placeholder"
  78. @change="val.change"
  79. class="w100"
  80. v-bind="val.comProps"
  81. v-model="state.innerModelValue[val.prop]"
  82. >
  83. </el-cascader>
  84. </el-form-item>
  85. </template>
  86. </el-col>
  87. <el-col :xs="12" :sm="9" :md="9" :lg="6" :xl="4" class="mb20">
  88. <el-form-item class="table-form-btn" label-width="auto">
  89. <div>
  90. <!-- 使用el-button-group会导致具有type属性的按钮的右边框无法显示 -->
  91. <!-- <el-button-group> -->
  92. <el-button plain type="primary" icon="ele-Search" @click="onSearch(tableSearchRef)"> 查询 </el-button>
  93. <el-button icon="ele-Refresh" @click="onReset(tableSearchRef)" style="margin-left: 12px"> 重置 </el-button>
  94. <!-- </el-button-group> -->
  95. </div>
  96. </el-form-item>
  97. </el-col>
  98. </el-row>
  99. <el-divider style="margin-top: 5px" v-if="search.length > 3">
  100. <el-button :icon="state.isToggle ? 'ele-ArrowUpBold' : 'ele-ArrowDownBold'" class="divider-btn" @click="state.isToggle = !state.isToggle"> </el-button>
  101. </el-divider>
  102. </el-form>
  103. </div>
  104. </template>
  105. <script setup lang="ts" name="makeTableDemoSearch">
  106. import { reactive, ref, watch } from 'vue';
  107. import type { FormInstance } from 'element-plus';
  108. import { dayjs } from 'element-plus';
  109. // 定义父组件传过来的值
  110. const props = defineProps({
  111. // 搜索表单,type-控件类型(input,select,cascader,date),options-type为selct时需传值,cascaderData,cascaderProps-type为cascader时需传值,属性同elementUI,cascaderProps不传则使用state默认。
  112. // 可带入comProps属性,和使用的控件属性对应
  113. search: {
  114. type: Array<TableSearchType>,
  115. default: () => [],
  116. },
  117. modelValue: {
  118. type: Object,
  119. default: () => ({}),
  120. },
  121. });
  122. // 定义子组件向父组件传值/事件
  123. const emit = defineEmits(['search', 'reset', 'update:modelValue']);
  124. // 定义变量内容
  125. const tableSearchRef = ref<FormInstance>();
  126. const state = reactive({
  127. isToggle: false,
  128. cascaderProps: { checkStrictly: true, emitPath: false, value: 'id', label: 'name', expandTrigger: 'hover' },
  129. /** 内部 modelValue */
  130. innerModelValue: {} as EmptyObjectType,
  131. });
  132. /** 监听 props.modelValue 变化 */
  133. watch(
  134. () => props.modelValue,
  135. (val) => {
  136. state.innerModelValue = val;
  137. }
  138. );
  139. /** 监听 state.innerModelValue 变化 */
  140. watch(
  141. () => state.innerModelValue,
  142. (val) => {
  143. emit('update:modelValue', val);
  144. },
  145. { deep: true }
  146. );
  147. // 查询
  148. const onSearch = (formEl: FormInstance | undefined) => {
  149. if (!formEl) return;
  150. formEl.validate((isValid: boolean): void => {
  151. if (!isValid) return;
  152. emit('search', state.innerModelValue);
  153. });
  154. };
  155. // 重置
  156. const onReset = (formEl: FormInstance | undefined) => {
  157. if (!formEl) return;
  158. formEl.resetFields();
  159. emit('reset', state.innerModelValue);
  160. };
  161. /** 时间范围默认时间 */
  162. const defaultTime = ref<[Date, Date]>([new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]);
  163. /** 时间范围快捷选择 */
  164. const shortcuts = [
  165. {
  166. text: '7天内',
  167. value: () => {
  168. const end = dayjs().endOf('day').toDate();
  169. const start = dayjs().startOf('day').add(-7, 'day').toDate();
  170. return [start, end];
  171. },
  172. },
  173. {
  174. text: '1个月内',
  175. value: () => {
  176. const end = dayjs().endOf('day').toDate();
  177. const start = dayjs().startOf('day').add(-1, 'month').toDate();
  178. return [start, end];
  179. },
  180. },
  181. {
  182. text: '3个月内',
  183. value: () => {
  184. const end = dayjs().endOf('day').toDate();
  185. const start = dayjs().startOf('day').add(-3, 'month').toDate();
  186. return [start, end];
  187. },
  188. },
  189. ];
  190. </script>
  191. <style scoped lang="scss">
  192. .table-search-container {
  193. display: flex;
  194. .table-form {
  195. flex: 1;
  196. .table-form-btn-toggle {
  197. white-space: nowrap;
  198. user-select: none;
  199. display: flex;
  200. align-items: center;
  201. color: var(--el-color-primary);
  202. }
  203. }
  204. }
  205. .divider-btn {
  206. height: 20px;
  207. border-radius: 10px;
  208. }
  209. </style>