tree.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. size="small"
  52. placeholder="远端数据字典网址"></el-input>
  53. 远程搜索
  54. <el-switch v-model="data.remote"></el-switch><br>
  55. 请求方法
  56. <el-select v-model="data.dicMethod"
  57. size="small"
  58. placeholder="请求方法"
  59. style="width: 100%;">
  60. <el-option label="POST"
  61. value="post"></el-option>
  62. <el-option label="GET"
  63. value="get"></el-option>
  64. </el-select>
  65. <p v-if="data.dicMethod == 'post'">
  66. 请求参数
  67. <avue-dynamic v-model="data.dicQueryConfig"
  68. :children="option"></avue-dynamic>
  69. </p>
  70. <p v-if="data.dicUrl">
  71. 返回结构
  72. <monaco-editor v-model="dicFormatter"
  73. height="80"
  74. :keyIndex="`dict-formatter-${data.prop}`"
  75. :options="options"></monaco-editor>
  76. </p>
  77. </el-tab-pane>
  78. </el-tabs>
  79. </div>
  80. </div>
  81. <div class="el-form-item el-form-item--small el-form--label-top">
  82. <label class="el-form-item__label"
  83. style="padding: 0;">字典key配置:</label>
  84. <div class="el-form-item__content">
  85. <ul>
  86. <li v-for="(value, key) in data.props"
  87. :key="key">
  88. <span style="width: 50px">{{ key }}</span>
  89. <el-input size="mini"
  90. v-model="data.props[key]"
  91. placeholder="key配置"></el-input>
  92. </li>
  93. </ul>
  94. </div>
  95. </div>
  96. <el-form-item v-if="data.dicOption == 'remote'"
  97. label="重新请求字典(crud)">
  98. <el-switch v-model="data.dicFlag"></el-switch>
  99. </el-form-item>
  100. <div class="el-form-item el-form-item--small el-form--label-top">
  101. <label class="el-form-item__label"
  102. style="padding: 0;">级联配置:</label>
  103. <div class="el-form-item__content">
  104. <draggable tag="ul"
  105. :list="data.cascaderItem|| data.cascader"
  106. :group="{ name: 'cascaderItem' }"
  107. ghost-class="ghost"
  108. handle=".drag-item">
  109. <li v-for="(item, index) in data.cascaderItem|| data.cascader"
  110. :key="index">
  111. <i class="drag-item el-icon-s-operation"
  112. style="font-size: 16px; margin: 0 5px; cursor: move;"></i>
  113. <el-input v-if="avueVersion('2.9.0')"
  114. size="mini"
  115. v-model="data.cascaderItem[index]"
  116. clearable
  117. placeholder="级联属性值"></el-input>
  118. <el-input v-else
  119. size="mini"
  120. v-model="data.cascader[index]"
  121. clearable
  122. placeholder="级联属性值"></el-input>
  123. <el-button @click="handleRemoveCascaderItemFields(index)"
  124. circle
  125. plain
  126. type="danger"
  127. size="mini"
  128. icon="el-icon-minus"
  129. style="padding: 4px; margin-left: 5px;">
  130. </el-button>
  131. </li>
  132. </draggable>
  133. <div style="margin-left: 22px;">
  134. <el-button type="text"
  135. @click="handleAddCascaderItemFields">添加列</el-button>
  136. </div>
  137. </div>
  138. </div>
  139. <template v-if="data.type == 'tree'">
  140. <el-form-item label="当有子级时,是否可选择父级"
  141. label-width="200px">
  142. <el-switch v-model="data.parent"></el-switch>
  143. </el-form-item>
  144. </template>
  145. <template v-if="data.type == 'cascader'">
  146. <el-form-item label="选项分隔符"
  147. label-width="100px">
  148. <el-input v-model="data.separator"
  149. clearable
  150. placeholder="选项分隔符"></el-input>
  151. </el-form-item>
  152. <el-form-item label="是否显示选中值的完整路径"
  153. label-width="200px">
  154. <el-switch v-model="data.showAllLevels"></el-switch>
  155. </el-form-item>
  156. <el-form-item label="是否可搜索"
  157. label-width="100px">
  158. <el-switch v-model="data.filterable"></el-switch>
  159. </el-form-item>
  160. </template>
  161. <el-form-item label="是否多选">
  162. <el-switch v-model="data.multiple"></el-switch>
  163. </el-form-item>
  164. <el-form-item v-if="data.type == 'tree' && data.multiple"
  165. label="勾选时,父子不关联"
  166. label-width="160px">
  167. <el-switch v-model="data.checkStrictly"></el-switch>
  168. </el-form-item>
  169. <el-dialog :visible.sync="dialogVisible"
  170. :rules="dialogRules"
  171. :before-close="beforeClose">
  172. <el-form ref="dialogForm"
  173. :model="dialogForm"
  174. label-width="80px">
  175. <el-form-item label="label">
  176. <el-input v-model="dialogForm.label"
  177. placeholder="label"></el-input>
  178. </el-form-item>
  179. <el-form-item label="value">
  180. <el-input v-model="dialogForm.value"
  181. placeholder="value"
  182. :type="this.dialogInputType">
  183. <el-select v-model="dialogInputType"
  184. slot="append"
  185. placeholder="数据类型"
  186. style="width: 100px">
  187. <el-option label="String"
  188. value="text"></el-option>
  189. <el-option label="Number"
  190. value="number"></el-option>
  191. </el-select>
  192. </el-input>
  193. </el-form-item>
  194. </el-form>
  195. <span slot="footer"
  196. class="dialog-footer">
  197. <el-button @click="dialogVisible = false">取 消</el-button>
  198. <el-button type="primary"
  199. @click="handleDialogAdd"
  200. v-if="dialogStatus == 'add'">确 定</el-button>
  201. <!-- <el-button type="primary" @click="handleDialogUpdate" v-else>确 定</el-button>-->
  202. </span>
  203. </el-dialog>
  204. </div>
  205. </template>
  206. <script>
  207. import MonacoEditor from '@utils/monaco-editor'
  208. export default {
  209. name: "config-tree",
  210. props: ['data'],
  211. components: { MonacoEditor },
  212. data() {
  213. return {
  214. dialogForm: {},
  215. dialogVisible: false,
  216. dialogRules: {
  217. label: { required: true, message: '请输入label' },
  218. value: { required: true, message: '请输入value' },
  219. },
  220. dialogStatus: 'add',
  221. selectData: undefined,
  222. dialogInputType: 'text',
  223. option: {
  224. column: [{
  225. type: 'input',
  226. prop: 'key',
  227. label: 'key'
  228. }, {
  229. type: 'input',
  230. prop: 'value',
  231. label: 'value'
  232. }]
  233. },
  234. options: {
  235. fullScreen: true,
  236. minimap: {
  237. enabled: false,
  238. },
  239. },
  240. dicFormatter: '',
  241. }
  242. },
  243. watch: {
  244. 'data.prop': {
  245. handler() {
  246. if (!this.avueVersion('2.9.0') && this.data.cascaderItem) {
  247. this.$set(this.data, 'cascader', this.data.cascaderItem)
  248. this.$delete(this.data, 'cascaderItem')
  249. }
  250. const { dicFormatter } = this.data
  251. this.dicFormatter = dicFormatter ? dicFormatter + '' : '(res) => {\r\n return res.data\r\n}'
  252. },
  253. immediate: true
  254. },
  255. dicFormatter: {
  256. handler(val) {
  257. try {
  258. this.data.dicFormatter = eval(val)
  259. } catch (e) {
  260. // console.error(e)
  261. }
  262. }
  263. }
  264. },
  265. methods: {
  266. handleRemoveCascaderItemFields(index) {
  267. if (this.avueVersion('2.9.0')) this.data.cascaderItem.splice(index, 1)
  268. else this.data.cascader.splice(index, 1)
  269. },
  270. handleAddCascaderItemFields() {
  271. if (this.avueVersion('2.9.0')) this.data.cascaderItem.push('')
  272. else {
  273. if (!this.data.cascader) {
  274. this.$set(this.data, 'cascader', [])
  275. delete this.data.cascaderItem
  276. }
  277. this.data.cascader.push('')
  278. }
  279. },
  280. handleTabClick({ name }) {
  281. if (name == 'remote' && !this.data.dicQueryConfig) this.data.dicQueryConfig = []
  282. },
  283. handleParentNodeAdd() {
  284. this.selectData = undefined
  285. this.dialogStatus = 'add';
  286. this.dialogVisible = true;
  287. },
  288. handleNodeAdd(data) {
  289. this.selectData = data;
  290. this.dialogStatus = 'add';
  291. this.dialogVisible = true;
  292. },
  293. handleNodeRemove(node, data) {
  294. const parent = node.parent;
  295. const children = parent.data.children || parent.data;
  296. const index = children.findIndex(d => d.id === data.id);
  297. children.splice(index, 1);
  298. },
  299. handleDialogAdd() {
  300. this.$refs.dialogForm.validate((valid) => {
  301. if (valid) {
  302. const { label, value } = this.dialogForm;
  303. const node = this.$refs.tree.getNode(value)
  304. if (node) this.$message.error("value重复")
  305. else {
  306. const data = this.selectData
  307. const newNode = {
  308. label,
  309. value: this.dialogInputType == 'number' ? new Number(value) : value,
  310. }
  311. if (data) {
  312. if (!data.children) this.$set(data, 'children', [])
  313. data.children.push(newNode)
  314. } else {
  315. this.$set(this.data.dicData, this.data.dicData.length, newNode)
  316. }
  317. this.beforeClose()
  318. }
  319. }
  320. })
  321. },
  322. beforeClose() {
  323. this.$refs.dialogForm.clearValidate()
  324. this.dialogForm = {}
  325. this.dialogVisible = false
  326. }
  327. },
  328. }
  329. </script>
  330. <style lang="scss" scoped>
  331. .custom-tree-node {
  332. flex: 1;
  333. display: flex;
  334. align-items: center;
  335. justify-content: space-between;
  336. font-size: 14px;
  337. padding-right: 8px;
  338. }
  339. </style>