tree.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <div>
  3. <el-form-item label="占位内容">
  4. <el-input v-model="data.placeholder"
  5. clearable
  6. placeholder="占位内容"></el-input>
  7. </el-form-item>
  8. <div class="el-form-item el-form-item--small el-form--label-top">
  9. <label class="el-form-item__label"
  10. style="padding: 0;">字典配置:</label>
  11. <div class="el-form-item__content">
  12. <el-tabs v-model="data.dicOption"
  13. stretch
  14. @tab-click="handleTabClick">
  15. <el-tab-pane label="静态数据"
  16. name="static">
  17. <el-tree ref="tree"
  18. :data="data.dicData"
  19. default-expand-all
  20. draggable
  21. node-key="value"
  22. :expand-on-click-node="false">
  23. <span class="custom-tree-node"
  24. slot-scope="{ node, data }">
  25. <span>{{ node.label }}</span>
  26. <span>
  27. <el-button type="text"
  28. size="mini"
  29. icon="el-icon-plus"
  30. @click="handleNodeAdd(data)"></el-button>
  31. <!-- <el-button class="warning" type="text" size="mini" icon="el-icon-edit"-->
  32. <!-- @click="handleNodeEdit(data)"></el-button>-->
  33. <el-button class="danger"
  34. type="text"
  35. size="mini"
  36. icon="el-icon-delete"
  37. @click="handleNodeRemove(node, data)"></el-button>
  38. </span>
  39. </span>
  40. </el-tree>
  41. <div style="margin-left: 22px;">
  42. <el-button type="text"
  43. @click="handleParentNodeAdd">添加父级
  44. </el-button>
  45. </div>
  46. </el-tab-pane>
  47. <el-tab-pane label="远端数据"
  48. name="remote">
  49. 网址
  50. <el-input v-model="data.dicUrl"
  51. placeholder="远端数据字典网址"></el-input>
  52. 远程搜索
  53. <el-switch v-model="data.remote"></el-switch><br>
  54. 请求方法
  55. <el-select v-model="data.dicMethod"
  56. placeholder="请求方法"
  57. style="width: 100%;">
  58. <el-option label="POST"
  59. value="post"></el-option>
  60. <el-option label="GET"
  61. value="get"></el-option>
  62. </el-select>
  63. <p v-if="data.dicMethod == 'post'">
  64. 请求参数
  65. <avue-dynamic v-model="data.dicQueryConfig"
  66. :children="option"></avue-dynamic>
  67. </p>
  68. </el-tab-pane>
  69. </el-tabs>
  70. </div>
  71. </div>
  72. <div class="el-form-item el-form-item--small el-form--label-top">
  73. <label class="el-form-item__label"
  74. style="padding: 0;">字典key配置:</label>
  75. <div class="el-form-item__content">
  76. <ul>
  77. <li v-for="(value, key) in data.props"
  78. :key="key">
  79. <span style="width: 50px">{{ key }}</span>
  80. <el-input size="mini"
  81. v-model="data.props[key]"
  82. placeholder="key配置"></el-input>
  83. </li>
  84. </ul>
  85. </div>
  86. </div>
  87. <el-form-item v-if="data.dicOption == 'remote'"
  88. label="重新请求字典(crud)">
  89. <el-switch v-model="data.dicFlag"></el-switch>
  90. </el-form-item>
  91. <div class="el-form-item el-form-item--small el-form--label-top">
  92. <label class="el-form-item__label"
  93. style="padding: 0;">级联配置:</label>
  94. <div class="el-form-item__content">
  95. <draggable tag="ul"
  96. :list="data.cascaderItem"
  97. :group="{ name: 'cascaderItem' }"
  98. ghost-class="ghost"
  99. handle=".drag-item">
  100. <li v-for="(item, index) in data.cascaderItem"
  101. :key="index">
  102. <i class="drag-item el-icon-s-operation"
  103. style="font-size: 16px; margin: 0 5px; cursor: move;"></i>
  104. <el-input size="mini"
  105. v-model="data.cascaderItem[index]"
  106. clearable
  107. placeholder="级联属性值"></el-input>
  108. <el-button @click="handleRemoveCascaderItemFields(index)"
  109. circle
  110. plain
  111. type="danger"
  112. size="mini"
  113. icon="el-icon-minus"
  114. style="padding: 4px; margin-left: 5px;">
  115. </el-button>
  116. </li>
  117. </draggable>
  118. <div style="margin-left: 22px;">
  119. <el-button type="text"
  120. @click="handleAddCascaderItemFields">添加列</el-button>
  121. </div>
  122. </div>
  123. </div>
  124. <template v-if="data.type == 'tree'">
  125. <el-form-item label="当有子级时,是否可选择父级"
  126. label-width="200px">
  127. <el-switch v-model="data.parent"></el-switch>
  128. </el-form-item>
  129. </template>
  130. <template v-if="data.type == 'cascader'">
  131. <el-form-item label="选项分隔符"
  132. label-width="100px">
  133. <el-input v-model="data.separator"
  134. clearable
  135. placeholder="选项分隔符"></el-input>
  136. </el-form-item>
  137. <el-form-item label="是否显示选中值的完整路径"
  138. label-width="200px">
  139. <el-switch v-model="data.showAllLevels"></el-switch>
  140. </el-form-item>
  141. <el-form-item label="是否可搜索"
  142. label-width="100px">
  143. <el-switch v-model="data.filterable"></el-switch>
  144. </el-form-item>
  145. </template>
  146. <el-form-item label="是否多选">
  147. <el-switch v-model="data.multiple"></el-switch>
  148. </el-form-item>
  149. <el-form-item label="是否禁用">
  150. <el-switch v-model="data.disabled"></el-switch>
  151. </el-form-item>
  152. <el-form-item label="是否可见">
  153. <el-switch v-model="data.display"></el-switch>
  154. </el-form-item>
  155. <el-form-item label="是否必填">
  156. <el-switch v-model="data.required"></el-switch>
  157. </el-form-item>
  158. <el-dialog :visible.sync="dialogVisible"
  159. :rules="dialogRules"
  160. :before-close="beforeClose">
  161. <el-form ref="dialogForm"
  162. :model="dialogForm"
  163. label-width="80px">
  164. <el-form-item label="label">
  165. <el-input v-model="dialogForm.label"
  166. placeholder="label"></el-input>
  167. </el-form-item>
  168. <el-form-item label="value">
  169. <el-input v-model="dialogForm.value"
  170. placeholder="value"
  171. :type="this.dialogInputType">
  172. <el-select v-model="dialogInputType"
  173. slot="append"
  174. placeholder="数据类型"
  175. style="width: 100px">
  176. <el-option label="String"
  177. value="text"></el-option>
  178. <el-option label="Number"
  179. value="number"></el-option>
  180. </el-select>
  181. </el-input>
  182. </el-form-item>
  183. </el-form>
  184. <span slot="footer"
  185. class="dialog-footer">
  186. <el-button @click="dialogVisible = false">取 消</el-button>
  187. <el-button type="primary"
  188. @click="handleDialogAdd"
  189. v-if="dialogStatus == 'add'">确 定</el-button>
  190. <!-- <el-button type="primary" @click="handleDialogUpdate" v-else>确 定</el-button>-->
  191. </span>
  192. </el-dialog>
  193. </div>
  194. </template>
  195. <script>
  196. export default {
  197. name: "config-tree",
  198. props: ['data'],
  199. data() {
  200. return {
  201. validator: {
  202. type: null,
  203. required: null,
  204. pattern: null,
  205. length: null
  206. },
  207. dialogForm: {},
  208. dialogVisible: false,
  209. dialogRules: {
  210. label: { required: true, message: '请输入label' },
  211. value: { required: true, message: '请输入value' },
  212. },
  213. dialogStatus: 'add',
  214. selectData: undefined,
  215. dialogInputType: 'text',
  216. option: {
  217. column: [{
  218. type: 'input',
  219. prop: 'key',
  220. label: 'key'
  221. }, {
  222. type: 'input',
  223. prop: 'value',
  224. label: 'value'
  225. }]
  226. },
  227. }
  228. },
  229. methods: {
  230. handleRemoveCascaderItemFields(index) {
  231. this.data.cascaderItem.splice(index, 1)
  232. },
  233. handleAddCascaderItemFields() {
  234. this.data.cascaderItem.push('')
  235. },
  236. generateRule() {
  237. const rules = [];
  238. Object.keys(this.validator).forEach(key => {
  239. if (this.validator[key]) rules.push(this.validator[key])
  240. })
  241. this.data.rules = rules
  242. },
  243. handleTabClick({ name }) {
  244. if (name == 'remote' && !this.data.dicQueryConfig) this.data.dicQueryConfig = []
  245. },
  246. handleParentNodeAdd() {
  247. this.selectData = undefined
  248. this.dialogStatus = 'add';
  249. this.dialogVisible = true;
  250. },
  251. handleNodeAdd(data) {
  252. this.selectData = data;
  253. this.dialogStatus = 'add';
  254. this.dialogVisible = true;
  255. },
  256. handleNodeRemove(node, data) {
  257. const parent = node.parent;
  258. const children = parent.data.children || parent.data;
  259. const index = children.findIndex(d => d.id === data.id);
  260. children.splice(index, 1);
  261. },
  262. handleDialogAdd() {
  263. this.$refs.dialogForm.validate((valid) => {
  264. if (valid) {
  265. const { label, value } = this.dialogForm;
  266. const node = this.$refs.tree.getNode(value)
  267. if (node) this.$message.error("value重复")
  268. else {
  269. const data = this.selectData
  270. const newNode = {
  271. label,
  272. value: this.dialogInputType == 'number' ? new Number(value) : value,
  273. }
  274. if (data) {
  275. if (!data.children) this.$set(data, 'children', [])
  276. data.children.push(newNode)
  277. } else {
  278. this.$set(this.data.dicData, this.data.dicData.length, newNode)
  279. }
  280. this.beforeClose()
  281. }
  282. }
  283. })
  284. },
  285. beforeClose() {
  286. this.$refs.dialogForm.clearValidate()
  287. this.dialogForm = {}
  288. this.dialogVisible = false
  289. }
  290. },
  291. watch: {
  292. 'data.required': function (val) {
  293. if (val) this.validator.required = { required: true, message: `请选择${this.data.label}` }
  294. else this.validator.required = null
  295. this.generateRule()
  296. },
  297. 'data.multiple': function (val) {
  298. if (val) this.data.defaultValue = []
  299. else delete this.data.defaultValue
  300. }
  301. }
  302. }
  303. </script>
  304. <style lang="scss" scoped>
  305. .custom-tree-node {
  306. flex: 1;
  307. display: flex;
  308. align-items: center;
  309. justify-content: space-between;
  310. font-size: 14px;
  311. padding-right: 8px;
  312. }
  313. </style>