index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. <template>
  2. <v-form ref="form" v-model="valid" class="login-form" @submit.prevent>
  3. <div>
  4. <v-row dense no-gutters class="justify-space-between">
  5. <template v-for="(item, index) in props.items.options">
  6. <slot :name="item.prevSlot"></slot>
  7. <v-col :key="item.key" v-if="!item.hide" :cols="item.col || '12'" class="position">
  8. <template v-if="item.slotTitle">
  9. <div :class="item.class" :style="item.slotTitleStyle">{{ item.slotTitle }}</div>
  10. </template>
  11. <div class="d-flex" :class="item.flexStyle || 'flex-row'">
  12. <v-text-field
  13. v-if="['text', 'password', 'number'].includes(item.type)"
  14. :type="item.type"
  15. v-model="item.value"
  16. :rules="item.rules"
  17. :disabled="item.disabled"
  18. :density="item.dense || 'compact'"
  19. :color="item.color"
  20. :label="item.label"
  21. :placeholder="item.placeholder || item.label"
  22. variant="outlined"
  23. :width="item.width"
  24. :autofocus="item.autofocus"
  25. :class="item.class"
  26. :suffix="item.suffix"
  27. :append-inner-icon="item.appendIcon"
  28. :clearable="item.clearable"
  29. :readonly="item.readonly"
  30. :prepend-inner-icon="item.prependInnerIcon"
  31. hide-spin-buttons
  32. @wheel="$event => handleWheel($event, item)"
  33. @keyup.enter="item.keyupEnterNative && item.keyupEnterNative(index)"
  34. @click="item.click && item.click(index)"
  35. @click:append-inner="item.clickAppendInner"
  36. @change="handleChange(item)"
  37. />
  38. <v-autocomplete
  39. v-if="item.type === 'autocomplete'"
  40. :rules="item.rules"
  41. v-model="item.value"
  42. :attach="!item.noAttach"
  43. :loading="item.loading"
  44. :label="item.label"
  45. :placeholder="item.placeholder || item.label"
  46. :items="item.items"
  47. :item-text="item.itemText || 'label'"
  48. :item-value="item.itemValue || 'value'"
  49. variant="outlined"
  50. :density="item.dense || 'compact'"
  51. :disabled="item.disabled"
  52. :multiple="item.multiple"
  53. :clearable="item.clearable"
  54. :search="item.searchInput"
  55. :hide-no-data="item.hideNoData"
  56. :no-data-text="item.noDataText || 'No data available'"
  57. :hide-selected="item.hideSelected"
  58. @change="handleChange(item)"
  59. >
  60. <template v-if="item.slotAppendItem" v-slot:append-item>
  61. <slot :name="item.slotAppendItem" :item="item"></slot>
  62. </template>
  63. <template v-if="item.prependItem" #prepend-item>
  64. <slot :name="item.prependItem" :item="item"></slot>
  65. </template>
  66. </v-autocomplete>
  67. <!-- autocomplete2 多选纸片样式 -->
  68. <v-autocomplete
  69. v-if="item.type === 'autocomplete2'"
  70. v-model="item.value"
  71. :rules="item.rules"
  72. :attach="!item.noAttach"
  73. :loading="item.loading"
  74. :label="item.label"
  75. :placeholder="item.placeholder || item.label"
  76. :items="item.canCreate ? [inputUpdateValue, ...item.items].filter(Boolean) : item.items"
  77. :item-text="item.itemText || 'label'"
  78. :item-value="item.itemValue || 'value'"
  79. variant="outlined"
  80. :density="item.dense || 'compact'"
  81. :multiple="item.multiple"
  82. :clearable="item.clearable"
  83. :search-input="item.searchInput"
  84. :hide-no-data="item.hideNoData"
  85. :hide-selected="item.hideSelected"
  86. :readonly="item.readonly"
  87. @change="handleChange(item)"
  88. @update:search-input="$event => item.canCreate ? inputUpdateValue = $event : inputUpdateAutocomplete($event)"
  89. :hide-details="!item.showDetails"
  90. deletable-chips
  91. cache-items
  92. small-chips
  93. ></v-autocomplete>
  94. <v-combobox
  95. v-if="item.type === 'combobox'"
  96. :rules="item.rules"
  97. v-model="item.value"
  98. :attach="true"
  99. :label="item.label"
  100. :placeholder="item.placeholder || item.label"
  101. :items="item.items"
  102. :item-text="item.itemText || 'label'"
  103. :item-value="item.itemValue || 'value'"
  104. variant="outlined"
  105. :density="item.dense || 'compact'"
  106. :clearable="item.clearable"
  107. :disabled="item.disabled"
  108. @change="handleChange(item)"
  109. >
  110. <template v-if="item.hasIcon" v-slot:selection="data">
  111. <v-icon color="blue darken-2">{{ data.item.label }}</v-icon>
  112. </template>
  113. <!-- <template v-if="item.hasIcon" v-slot:item="data">
  114. <v-list-item-avatar>
  115. <v-icon>{{ data.item.label }}</v-icon>
  116. </v-list-item-avatar>
  117. <v-list-item-content>
  118. {{ data.item.label }}
  119. </v-list-item-content>
  120. </template> -->
  121. </v-combobox>
  122. <v-textarea
  123. v-if="item.type === 'textarea'"
  124. :rules="item.rules"
  125. v-model="item.value"
  126. :label="item.label"
  127. :placeholder="item.placeholder || item.label"
  128. :no-resize="!item.resize"
  129. variant="outlined"
  130. :density="item.dense || 'compact'"
  131. :rows="item.rows || 3"
  132. :disabled="item.disabled"
  133. @change="handleChange(item)"
  134. ></v-textarea>
  135. <v-radio-group
  136. v-if="item.type === 'ifRadio'"
  137. v-model="item.value"
  138. :disabled="item.disabled"
  139. mandatory
  140. row
  141. @change="handleChange(item)"
  142. >
  143. <template v-slot:label>
  144. <div :style="`width: ${item.width || 120}px;`">{{ item.label }}</div>
  145. </template>
  146. <v-radio
  147. v-for="radio in item.items"
  148. :key="`${item.key}_radio_${radio.label}`"
  149. :readonly="radio.readonly"
  150. :label="radio.label"
  151. :value="radio.value"
  152. class="mr-8"
  153. ></v-radio>
  154. </v-radio-group>
  155. <template v-if="item.type === 'checkbox'">
  156. <div style="width: 120px;" class="mt-4 label text-left">{{ item.label }}</div>
  157. <div :style="item.style">
  158. <v-checkbox
  159. v-model="item.value"
  160. v-for="k in item.items"
  161. :key="k.key"
  162. :label="k.label"
  163. :color="item.color"
  164. :value="k.value"
  165. :readonly="k.readonly"
  166. hide-details
  167. :multiple="true"
  168. class="mr-3"
  169. ></v-checkbox>
  170. </div>
  171. </template>
  172. <v-file-input
  173. v-if="item.type === 'upload'"
  174. :prepend-icon="item.prependIcon || ''"
  175. :append-icon="item.appendIcon"
  176. :append-outer-icon="item.appendOuterIcon"
  177. :show-size="item.showSize"
  178. variant="outlined"
  179. :density="item.dense || 'compact'"
  180. v-model="item.value"
  181. :placeholder="item.placeholder || item.label"
  182. :hint="item.hint"
  183. :rules="item.rules"
  184. :label="item.label"
  185. :persistent-hint="item.persistentHint"
  186. :loading= "item.loading"
  187. :disabled="item.disabled"
  188. :multiple="item.multiple"
  189. :success="item.success"
  190. :error="item.error"
  191. :accept="item.accept || '.xlsx, .xls, .csv, .pdf, .txt, .doc'"
  192. @change="handleChange(item)"
  193. >
  194. <template v-if="item.selfAppend" #append>
  195. <slot :name="item.selfAppend" :data="item.value"></slot>
  196. </template>
  197. </v-file-input>
  198. <v-color-picker
  199. v-if="item.type === 'colorPicker'"
  200. class="mb-5"
  201. v-model="item.value"
  202. :elevation="item.elevation || 5"
  203. :dot-size="item.dotSize || 25"
  204. :show-swatches="item.showSwatches || false"
  205. swatches-max-height="200"
  206. :mode="item.mode || 'hexa'"
  207. :hide-mode-switch="true"
  208. @input="item.change"
  209. />
  210. <template v-if="item.type === 'switch'">
  211. <span v-if="item.describe"> {{ item.describe }} </span>
  212. <span class="ml-2" v-if="item.trueLabel"> {{ item.trueLabel }}</span>
  213. <v-switch
  214. dense hide-details class="mt-0 ml-2 pa-0"
  215. v-model="item.value"
  216. :label="item.label"
  217. :disabled="item.disabled || false"
  218. :color="item.color || 'primary'"
  219. :true-value="(item.trueValue !== undefined) ? item.trueValue : true"
  220. :false-value="(item.falseValue !== undefined) ? item.falseValue : false"
  221. ></v-switch>
  222. <span v-if="item.falseLabel"> {{ item.falseLabel }} </span>
  223. </template>
  224. <template v-if="item.type === 'date'">
  225. <div class="d-flex" style="margin-bottom: 22px;">
  226. <span class="label d-flex align-center" :style="`width: ${item.width || 120}px;`">{{ item.label }}</span>
  227. <!-- <date-picker
  228. :is-valid="isValid"
  229. :option="{ ...item.option, disabled: item.disabled }"
  230. v-model="item.value"
  231. :style="item.style"
  232. @change="item.value = $event; handleChange(item); handleCheck(item)"></date-picker> -->
  233. </div>
  234. </template>
  235. <template v-if="item.slotName">
  236. <slot :name="item.slotName" :item="item"></slot>
  237. </template>
  238. </div>
  239. </v-col>
  240. </template>
  241. </v-row>
  242. </div>
  243. <slot></slot>
  244. </v-form>
  245. </template>
  246. <script setup>
  247. // import DatePicker from '@/components/Form/datePicker.vue'
  248. import { ref, defineEmits } from 'vue'
  249. defineOptions({ name: 'form-index' })
  250. const props = defineProps({items: Object})
  251. const inputUpdateValue = ref('')
  252. const form = ref()
  253. const valid = ref(false)
  254. const isValid = ref(true)
  255. const emit = defineEmits(['inputUpdateAutocomplete', 'change'])
  256. const handleWheel = (event, item) => {
  257. if (item.type !== 'number') return
  258. event.preventDefault()
  259. if (event.deltaY > 0) {
  260. item.value--
  261. } else {
  262. item.value++
  263. }
  264. handleChange(item)
  265. }
  266. const handleCheck = (e) => {
  267. if (e.type !== 'date' || e.hide || !e.rules) return
  268. const rules = e.rules[0]
  269. const check = rules(e.value)
  270. if (typeof check === 'string') {
  271. e.option.error = true
  272. e.option.errorMsg = check
  273. return
  274. }
  275. e.option.error = false
  276. e.option.errorMsg = null
  277. }
  278. const validateTime = () => {
  279. isValid.value = true
  280. props.items.options.forEach((e) => {
  281. if (e.type !== 'date' || e.hide || !e.rules) return
  282. const rules = e.rules[0]
  283. const check = rules(e.value)
  284. if (typeof check === 'string') {
  285. isValid.value = false
  286. e.option.error = true
  287. e.option.errorMsg = check
  288. } else {
  289. e.option.error = false
  290. e.option.errorMsg = null
  291. }
  292. })
  293. return isValid.value
  294. }
  295. const validate = () => {
  296. const form = form.value.validate()
  297. const time = validateTime()
  298. return form && time
  299. }
  300. const inputUpdateAutocomplete = (val) => {
  301. emit('inputUpdateAutocomplete', val)
  302. }
  303. const resetValidation = () => {
  304. form.value.resetValidation()
  305. }
  306. const reset = () => {
  307. form.value.reset()
  308. }
  309. const handleChange = (item) => {
  310. if (item.type === 'date' && item.value) item.option.validate = false
  311. if (item?.change) item.change(item.value, item)
  312. emit('change', false)
  313. }
  314. </script>
  315. <style lang="scss" scoped>
  316. .position {
  317. position: relative;
  318. }
  319. .label {
  320. font-size: 14px;
  321. color: rgba(0, 0, 0, .6);
  322. }
  323. </style>