index.vue 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878
  1. <template>
  2. <div class="app-container">
  3. <el-row :gutter="20">
  4. <el-col :span="18" :class="{ edit: menudata }">
  5. <el-card shadow="always" :body-style="{ padding: '20px' }">
  6. <template #header>
  7. <div>
  8. <span>{{ $t('tableMange.fieldEdit') }}</span>
  9. </div>
  10. </template>
  11. <!-- <Queryfrom
  12. :form-vals="checkedWhere"
  13. @getList="getList"
  14. ref="mychild"
  15. /> -->
  16. <el-select v-model="tableName" :placeholder="$t('tableMange.selectTable')" filterable @change="getList" class="mb10">
  17. <el-option v-for="item in tableList" :key="item.tableName" :label="item.tableComment"
  18. :value="item.tableName">
  19. <span class="discribe" style="float: left">{{
  20. item.tableComment
  21. }}</span>
  22. <span style="float: right; color: #8492a6; font-size: 13px">{{
  23. item.tableName
  24. }}</span>
  25. </el-option>
  26. </el-select>
  27. <!-- <el-form
  28. :model="tableFieldList"
  29. ref="tableForm"
  30. :rules="rules"
  31. :inline="false"
  32. size="normal"
  33. > -->
  34. <el-table :data="tableFieldList" border ref="dragTable" row-key="id" max-height="500px">
  35. <!--
  36. :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
  37. -->
  38. >
  39. <el-table-column type="index" :label="$t('tableMange.serialNumber')" width="50" class-name="allowDrag">
  40. </el-table-column>
  41. <el-table-column align="center" prop="fieldName" :label="$t('tableMange.dataField')">
  42. </el-table-column>
  43. <el-table-column align="center" prop="fieldDescription" :label="$t('tableMange.fieldDescription')">
  44. <template slot-scope="scope">
  45. <!-- <el-form-item size="normal" prop="fieldDescription"> -->
  46. <input :class="{
  47. isNullDesc:
  48. scope.row.fieldDescription == '' &&
  49. scope.row.isShow &&
  50. isInputInvalid
  51. ? true
  52. : false,
  53. ipt: true,
  54. }" v-model="scope.row.fieldDescription" />
  55. <!-- <el-input v-model="scope.row.fieldDescription" /> -->
  56. <!-- </el-form-item> -->
  57. </template>
  58. </el-table-column>
  59. <el-table-column align="center" prop="relationTable" :label="$t('tableMange.relatedTable')">
  60. <template slot-scope="scope">
  61. <el-select v-model="scope.row.relationTable" :disabled="!scope.row.relationTableList ||
  62. !scope.row.relationTableList.length
  63. " :placeholder="$t('tableMange.selectTable')" clearable filterable @change="ralationTableChange(scope.row)">
  64. <el-option v-for="item in scope.row.relationTableList" :key="item.tableName"
  65. :label="item.tableComment" :value="item.tableName">
  66. <span style="float: left">{{ item.tableComment }}</span>
  67. <span style="float: right; color: #8492a6; font-size: 13px">{{ item.tableName }}</span>
  68. </el-option>
  69. </el-select>
  70. </template>
  71. </el-table-column>
  72. <el-table-column align="center" prop="relationFieldName" :label="$t('tableMange.relatedField')">
  73. <template slot-scope="scope">
  74. <el-select v-model="scope.row.relationFieldName" @change="relationFieldChange(scope.row)"
  75. :disabled="!scope.row.disableRelaFieldName" :placeholder="$t('tableMange.relatedField')" filterable>
  76. <el-option v-for="item in scope.row.relaFieldNameList" :key="item.fieldName"
  77. :label="item.fieldDescription" :value="item.fieldName">
  78. </el-option>
  79. </el-select>
  80. </template>
  81. </el-table-column>
  82. <el-table-column align="center" prop="relationType" :label="$t('tableMange.relatedType')">
  83. <template slot-scope="scope">
  84. <el-select v-model="scope.row.relationType" :placeholder="$t('tableMange.relatedType')" :disabled="!scope.row.disableRelaType"
  85. @change="relationTypeChangeHandler(scope.row)" filterable>
  86. <el-option v-for="item in relaTypeList" :key="item.value" :label="item.label" :value="item.value">
  87. </el-option>
  88. </el-select>
  89. </template>
  90. </el-table-column>
  91. <!-- <el-table-column prop="relationShowField" label="关联显示字段">
  92. <template slot-scope="scope">
  93. <el-select
  94. v-model="scope.row.relationShowField"
  95. :disabled="!scope.row.disableRelaFieldName"
  96. placeholder="请选择"
  97. clearable
  98. filterable
  99. multiple
  100. >
  101. <el-option
  102. v-for="item in scope.row.relaFieldNameList"
  103. :key="item.fieldName"
  104. :label="item.fieldDescription"
  105. :value="item.fieldName + ' ' + item.fieldDescription"
  106. >
  107. </el-option>
  108. </el-select>
  109. </template>
  110. </el-table-column> -->
  111. <el-table-column align="center" prop="isShow" :label="$t('tableMange.isShow')">
  112. <template slot-scope="scope">
  113. <el-switch v-model="scope.row.isShow"></el-switch>
  114. </template>
  115. </el-table-column>
  116. <el-table-column align="center" prop="isSearch" :label="$t('tableMange.isSearch')">
  117. <template slot-scope="scope">
  118. <el-switch v-model="scope.row.isSearch"></el-switch>
  119. </template>
  120. </el-table-column>
  121. <el-table-column align="center" prop="isExport" :label="$t('tableMange.isExport')">
  122. <template slot-scope="scope">
  123. <el-switch v-model="scope.row.isExport"></el-switch>
  124. </template>
  125. </el-table-column>
  126. </el-table>
  127. </el-card>
  128. </el-col>
  129. <el-col :span="6" :class="{ menudata: menudata }">
  130. <el-tabs v-model="activeName" @tab-click="tabhandleClick">
  131. <el-tab-pane :label="$t('tableMange.menuEdit')" name="menuedit">
  132. <el-card shadow="always" :body-style="{ padding: '10px' }">
  133. <el-form ref="formData" :model="formData" label-width="100px" :rules="rules">
  134. <el-form-item :label="$t('tableMange.menuType')" prop="dtType">
  135. <el-select v-model="formData.dtType" :placeholder="$t('tableMange.menuType')">
  136. <el-option :label="$t('tableMange.tableMenu')" value="0"></el-option>
  137. <el-option :label="$t('tableMange.treeMenu')" value="3"></el-option>
  138. </el-select>
  139. </el-form-item>
  140. <el-form-item :label="$t('tableMange.menuName')" prop="menuName">
  141. <el-input v-model="formData.menuName"></el-input>
  142. </el-form-item>
  143. <el-form-item :label="$t('tableMange.menuRoute')" prop="routePath" v-if="formData.dtType === '0'">
  144. <treeselect :append-to-body="true" v-model="formData.routePath" :options="menus"
  145. :normalizer="normalizer" @change="treeSelectChange" @focus="treeSelectFocus" :show-count="true"
  146. :placeholder="$t('tableMange.selectParentRoute')" />
  147. </el-form-item>
  148. <el-form-item :label="$t('tableMange.showList')" v-if="false">
  149. <el-switch v-model="formData.isShowList"></el-switch>
  150. </el-form-item>
  151. <el-form-item :label="$t('tableMange.timeFormat')" prop="timeFormate">
  152. <el-select v-model="formData.timeFormate" :placeholder="$t('tableMange.timeFormat')">
  153. <el-option v-for="val in dict.type.sys_time_format" :key="val.value" :label="val.label"
  154. :value="val.value"></el-option>
  155. </el-select>
  156. </el-form-item>
  157. <el-form-item :label="$t('tableMange.tablePrimaryKey')" prop="primaryKey">
  158. <el-select v-model="formData.primaryKey" :placeholder="$t('tableMange.tablePrimaryKey')">
  159. <el-option v-for="val in primaryKeyList" :key="val.fieldName" :label="val.fieldDescription"
  160. :value="val.fieldName"></el-option>
  161. </el-select>
  162. </el-form-item>
  163. <!-- <el-form-item label="操作列按钮" prop="btnGroupList">
  164. <el-select v-model="formData.btnGroupList" placeholder="请选择按钮组" clearable multiple>
  165. <el-option v-for="val in btnGroupOptions" :key="val.btnKey" :label="val.btnGroupName"
  166. :value="val.btnKey"></el-option>
  167. </el-select>
  168. </el-form-item> -->
  169. <el-form-item :label="$t('tableMange.sortDependency')">
  170. <el-select v-model="formData.orderByColumn" :placeholder="$t('tableMange.sortDependency')">
  171. <el-option v-for="val in orderByFieldList" :key="val.fieldName" :label="val.fieldDescription"
  172. :value="val.tableName + '.' + val.fieldName"></el-option>
  173. </el-select>
  174. </el-form-item>
  175. <el-form-item :label="$t('tableMange.sortMethod')" v-show="formData.orderByColumn">
  176. <el-radio v-model="formData.isAsc" :label="false">{{ $t('tableMange.ascending') }}
  177. </el-radio>
  178. <el-radio v-model="formData.isAsc" :label="true">{{ $t('tableMange.descending') }}
  179. </el-radio>
  180. </el-form-item>
  181. <el-form-item>
  182. <el-button size="mini" @click="previewHandle">{{ $t('tableMange.preview') }}</el-button>
  183. <el-button size="mini" type="primary" @click="createHandle">{{
  184. tId ? $t('tableMange.confirmModify') : $t('tableMange.confirmCreate')
  185. }}
  186. </el-button>
  187. </el-form-item>
  188. </el-form>
  189. </el-card>
  190. </el-tab-pane>
  191. <el-tab-pane :label="$t('tableMange.dataStatistics')" name="datacount">
  192. <el-button type="primary" class="inline-large-button" icon="el-icon-plus" size="mini"
  193. @click="addDataDialog">
  194. {{ $t('tableMange.addDataField') }}
  195. </el-button>
  196. <el-table :data="dragTableStatisticList" border class="mb10" style="width: 100%">
  197. style="width: 100%">
  198. <el-table-column type="index" :label="$t('tableMange.serialNumber')" width="50" class-name="allowDrag">
  199. </el-table-column>
  200. <el-table-column prop="statisticTitle" :label="$t('tableMange.statisticTitle')">
  201. </el-table-column>
  202. <el-table-column prop="statisticField" :label="$t('tableMange.statisticData')">
  203. </el-table-column>
  204. <el-table-column prop="statisticType" :label="$t('tableMange.statisticType')">
  205. </el-table-column>
  206. <el-table-column :label="$t('tableMange.operation')" align="center" class-name="small-padding fixed-width">
  207. <template slot-scope="scope">
  208. <el-dropdown>
  209. <el-button type="warning" plain size="small">
  210. {{ $t('tableMange.handle') }}<i class="el-icon-arrow-down el-icon--right"></i>
  211. </el-button>
  212. <el-dropdown-menu slot="dropdown">
  213. <el-dropdown-item>
  214. <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdateStat(scope.row)">{{ $t('tableMange.modify') }}
  215. </el-button>
  216. </el-dropdown-item>
  217. <el-dropdown-item>
  218. <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDeleteStat(scope.row)">{{ $t('tableMange.delete') }}
  219. </el-button>
  220. </el-dropdown-item>
  221. </el-dropdown-menu>
  222. </el-dropdown>
  223. </template>
  224. </el-table-column>
  225. </el-table>
  226. </el-tab-pane>
  227. <el-tab-pane :label="$t('tableMange.styleEdit')" name="styleEdit">
  228. <StyleFormPanel ref="styleTableRef" :tableFieldList="tableFieldList"
  229. :dragTableStyleList="dragTableStyleList" />
  230. </el-tab-pane>
  231. <el-tab-pane :label="$t('tableMange.dataFilter')" name="dataFilter">
  232. <DataFilterPanel ref="dataFilterRef" :tableFieldList="tableFieldList" :filterDataEcho="filterDataEcho">
  233. </DataFilterPanel>
  234. </el-tab-pane>
  235. <el-tab-pane :label="$t('tableMange.classificationQuery')" name="classificationQuery">
  236. <ClassificationQueryPanel ref="classificationQueryRef" :tableFieldList="tableFieldList"
  237. :classificationDataEcho="classificationDataEcho"></ClassificationQueryPanel>
  238. </el-tab-pane>
  239. </el-tabs>
  240. </el-col>
  241. </el-row>
  242. <el-dialog :title="$t('tableMange.effectPreview')" :visible.sync="isShowPreview" width="50%">
  243. <div class="cardBox">
  244. <el-card shadow="hover" :body-style="{ padding: '20px' }" class="card"
  245. v-for="(item, index) in deepragTableStatisticList" :key="index">
  246. <el-tooltip class="item" effect="dark" :content="item.statisticTitle" placement="top-start">
  247. <div class="title">
  248. {{
  249. item.statisticTitle
  250. ? item.statisticTitle
  251. : item.statisticDescription
  252. }}
  253. </div>
  254. </el-tooltip>
  255. <div class="type">
  256. <div class="count">{{ $t('tableMange.statisticData') }}</div>
  257. </div>
  258. </el-card>
  259. </div>
  260. <el-row :gutter="10" class="mb8">
  261. <el-col :span="18">
  262. <el-input :placeholder="$t('tableMange.search')" clearable />
  263. </el-col>
  264. <el-col :span="6" class="previewbtn">
  265. <el-button type="primary" icon="el-icon-search" size="small" @click="() => { }">{{ $t('tableMange.search') }}
  266. </el-button>
  267. <el-button icon="el-icon-refresh" size="small" @click="() => { }">{{ $t('tableMange.reset') }}
  268. </el-button>
  269. </el-col>
  270. </el-row>
  271. <el-row :gutter="10" class="mb8">
  272. <el-col :span="1.5">
  273. <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="() => { }">{{ $t('tableMange.add') }}
  274. </el-button>
  275. </el-col>
  276. <el-col :span="1.5">
  277. <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="false" @click="() => { }">{{ $t('tableMange.edit') }}
  278. </el-button>
  279. </el-col>
  280. <el-col :span="1.5">
  281. <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="false" @click="() => { }">{{ $t('tableMange.delete') }}
  282. </el-button>
  283. </el-col>
  284. <el-col :span="1.5">
  285. <el-button plain icon="el-icon-upload2" size="mini" @click="() => { }">{{ $t('tableMange.import') }}
  286. </el-button>
  287. </el-col>
  288. <el-col :span="1.5">
  289. <el-button type="warning" plain icon="el-icon-download" size="mini" @click="() => { }">{{ $t('tableMange.export') }}
  290. </el-button>
  291. </el-col>
  292. </el-row>
  293. <el-table :data="tableDataList" @selection-change="() => { }">
  294. <el-table-column type="selection" width="55" align="center"></el-table-column>
  295. <el-table-column v-for="item in columns" :key="item.key + item.value" :label="item.value" align="center"
  296. :prop="toUpperCase(item.key)" />
  297. <el-table-column :label="$t('tableMange.operation')" align="center" class-name="small-padding fixed-width">
  298. <template>
  299. <el-dropdown>
  300. <el-button type="warning" plain size="small">
  301. {{ $t('tableMange.handle') }}<i class="el-icon-arrow-down el-icon--right"></i>
  302. </el-button>
  303. <el-dropdown-menu slot="dropdown">
  304. <el-dropdown-item>
  305. <el-button size="mini" type="text" icon="el-icon-edit" @click="() => { }">{{ $t('tableMange.modify') }}
  306. </el-button>
  307. </el-dropdown-item>
  308. <el-dropdown-item>
  309. <el-button size="mini" type="text" icon="el-icon-delete" @click="() => { }">{{ $t('tableMange.delete') }}
  310. </el-button>
  311. </el-dropdown-item>
  312. </el-dropdown-menu>
  313. </el-dropdown>
  314. </template>
  315. </el-table-column>
  316. </el-table>
  317. <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
  318. @pagination="pageList" />
  319. <template #footer>
  320. <span>
  321. <el-button @click="isShowPreview = false">{{ $t('tableMange.close') }}</el-button>
  322. <el-button type="primary" @click="createHandle">{{
  323. tId ? $t('tableMange.confirmModify') : $t('tableMange.confirmCreate')
  324. }}</el-button>
  325. </span>
  326. </template>
  327. </el-dialog>
  328. <!-- 添加数据统计对话框 -->
  329. <el-dialog :title="staictitle" :visible.sync="isShowAddData" width="30%">
  330. <el-form label-width="100px" :model="dataCountFormData">
  331. <el-form-item :label="$t('tableMange.statisticTitle')" prop="statisticTitle">
  332. <el-input v-model="dataCountFormData.statisticTitle"></el-input>
  333. </el-form-item>
  334. <el-form-item :label="$t('tableMange.statisticData')">
  335. <el-select v-model="dataCountFormData.statisticField" :placeholder="$t('tableMange.selectStatisticField')">
  336. <el-option v-for="item in dataArr" :key="item.id" :label="item.fieldName"
  337. :value="item.tableName + '.' + item.fieldName">
  338. <span class="discribe" style="float: left; margin-right: 5px">{{ isShowTableName }}
  339. </span>
  340. <span style="float: right; color: #8492a6; font-size: 13px">{{
  341. item.fieldName
  342. }}</span>
  343. </el-option>
  344. </el-select>
  345. </el-form-item>
  346. <el-form-item :label="$t('tableMange.statisticType')">
  347. <el-select v-model="dataCountFormData.statisticType" :placeholder="$t('tableMange.selectStatisticType')">
  348. <el-option v-for="item in dataType" :key="item.dictCode" :label="item.dictLabel"
  349. :value="item.dictValue"></el-option>
  350. </el-select>
  351. </el-form-item>
  352. </el-form>
  353. <span slot="footer" class="dialog-footer">
  354. <el-button @click="closeAddDialog">{{ $t('tableMange.cancel') }}</el-button>
  355. <el-button v-if="staictitle == $t('tableMange.modify')" type="primary" @click="upadtaData">{{ $t('tableMange.confirm') }}</el-button>
  356. <el-button v-else type="primary" @click="addData">{{ $t('tableMange.confirm') }}</el-button>
  357. </span>
  358. </el-dialog>
  359. </div>
  360. </template>
  361. <script>
  362. import {
  363. delTableData,
  364. dragTableInfo,
  365. listTable,
  366. unionListTableData,
  367. getInfoBySqlKey,
  368. addTableData,
  369. addStatistic,
  370. updateStatistic,
  371. insertByDefaultBtn,
  372. } from "@/api/tablelist/commonTable";
  373. import { getDicts } from "@/api/system/dict/data";
  374. import {
  375. getFormName,
  376. getListName,
  377. dragTablePreview,
  378. } from "@/api/dragform/form.js";
  379. import { getTableInfo, editTable } from "@/api/system/table.js";
  380. import { addDragTable } from "@/api/tablelist/commonTable.js";
  381. import { getParticMenu, updateMenu } from "@/api/system/menu.js";
  382. import { getMenuList, addMenu, tbnHasPerms } from "@/api/menu.js";
  383. import Queryfrom from "@/views/tablelist/commonTable/queryfrom.vue";
  384. import { mapState } from "vuex";
  385. import Sortable from "sortablejs";
  386. import Treeselect from "@riophae/vue-treeselect";
  387. import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  388. import { v4 as uuidv4 } from "uuid";
  389. import { listBtn } from "@/api/system/btn";
  390. import StyleFormPanel from "./components/StyleFormPanel.vue";
  391. import DataFilterPanel from "./components/DataFilterPanel.vue";
  392. import ClassificationQueryPanel from "./components/ClassificationQueryPanel.vue";
  393. export default {
  394. name: "tableMange",
  395. dicts: ["sys_time_format", "table_statistic_type"],
  396. props: [],
  397. components: {
  398. Queryfrom,
  399. Treeselect,
  400. StyleFormPanel,
  401. DataFilterPanel,
  402. ClassificationQueryPanel,
  403. },
  404. data() {
  405. return {
  406. menuList: [], //路由数组
  407. btnTemplate: [], //按钮模板
  408. isNeedNewMenu: false, //是否需要新菜单
  409. filterDataEcho: "", //数据过滤回显数据
  410. classificationDataEcho: "", //分类查询回显数据
  411. staictitle: "添加统计数据字段",
  412. isInputInvalid: false,
  413. // 修改表格时的menuId
  414. menuId: "",
  415. // 修改表格的id
  416. tId: "",
  417. // 当前模板信息
  418. templateInfo: {},
  419. // 排序方式 默认降序
  420. sortState: true,
  421. // 列信息
  422. columns: {},
  423. // 查询条件
  424. queryFromWhere: {},
  425. // 当前table唯一标识
  426. sqlKey: "",
  427. // 动态数据sqlkey
  428. staticSqlKey: "",
  429. // 表格的高度
  430. tableKey: "",
  431. // tableHeight: document.documentElement.scrollHeight - 245 + "px",
  432. tableList: [], //所有表格列表
  433. tableFieldList: [], // 当前表格字段数据
  434. tableName: "", // 当前表名称
  435. relationTableList: [], //关联表格列表
  436. relationFieldList: [], //关联字段列表
  437. relationTypeList: [], //关联方式列表
  438. total: 0, //表格数据总数
  439. relaTypeList: [
  440. {
  441. label: "等值连接",
  442. value: "INNER JOIN",
  443. },
  444. {
  445. label: "左连接",
  446. value: "LEFT JOIN",
  447. },
  448. {
  449. label: "右连接",
  450. value: "RIGHT JOIN",
  451. },
  452. ],
  453. headerList: [
  454. {
  455. prop: "fieldName",
  456. label: this.$t('tableMange.dataField')
  457. },
  458. {
  459. prop: "fieldDescription",
  460. label: this.$t('tableMange.fieldDescription')
  461. },
  462. {
  463. prop: "relationTable",
  464. label: this.$t('tableMange.relatedTable')
  465. },
  466. {
  467. prop: "relationFieldName",
  468. label: this.$t('tableMange.relatedField')
  469. },
  470. {
  471. prop: "relationType",
  472. label: this.$t('tableMange.relatedType')
  473. },
  474. {
  475. prop: "isShow",
  476. label: this.$t('tableMange.isShow')
  477. },
  478. {
  479. prop: "isSearch",
  480. label: this.$t('tableMange.isSearch')
  481. },
  482. {
  483. prop: "isExport",
  484. label: this.$t('tableMange.isExport')
  485. },
  486. {
  487. prop: "isCount",
  488. label: this.$t('tableMange.statisticData')
  489. }
  490. ], //表头列表
  491. // 右侧筛选条件勾选数据
  492. // checkedList: [], //勾选的查询框
  493. // checkedWhere:[],//需要渲染的查询表单
  494. // 右侧表单编辑
  495. formData: {
  496. menuName: "",
  497. routePath: undefined,
  498. isShowList: true,
  499. timeFormate: "",
  500. orderByColumn: "",
  501. isAsc: false,
  502. primaryKey: "",
  503. btnGroupList: [],
  504. dtType: "",
  505. },
  506. rules: {
  507. dtType: [
  508. { required: true, message: this.$t('tableMange.selectMenuType'), trigger: "change" }
  509. ],
  510. menuName: [
  511. { required: true, message: this.$t('tableMange.pleaseEnterMenuName'), trigger: "blur" }
  512. ],
  513. routePath: [
  514. { required: true, message: this.$t('tableMange.selectParentRoute'), trigger: "change" }
  515. ],
  516. timeFormate: [
  517. { required: true, message: this.$t('tableMange.selectTimeFormat'), trigger: "change" }
  518. ],
  519. primaryKey: [
  520. { required: true, message: this.$t('tableMange.selectTablePrimaryKey'), trigger: "blur" }
  521. ],
  522. fieldDescription: [
  523. { required: true, message: this.$t('tableMange.fieldDescriptionErrorNotNull'), trigger: "blur" }
  524. ]
  525. },
  526. menus: [], //路由列表数据
  527. // 预览弹窗相关
  528. isShowPreview: false, //弹窗显示与隐藏
  529. queryParams: {
  530. pageNum: 1,
  531. pageSize: 10,
  532. orderByColumn: "",
  533. isAsc: false,
  534. basicMap: {
  535. sql: "",
  536. },
  537. },
  538. loading: false, //表格加载
  539. tableDataList: [], //数据
  540. editData: {},
  541. menuOrderNum: 0,
  542. // tab动态切换
  543. activeName: "menuedit",
  544. // 数据统计对象
  545. dataCountFormData: {},
  546. // 添加数据统计表格
  547. dragTableStatisticList: [],
  548. deepragTableStatisticList: [],
  549. tableSqlList: [],
  550. searchFieldList: [],
  551. // 是否切换到数据统计
  552. menudata: false,
  553. // 显示添加字段对话框dataArr
  554. isShowAddData: false,
  555. dataType: [],
  556. uuid: "",
  557. // 样式编辑tab数据
  558. dragTableStyleList: [],
  559. //操作列按钮组数据
  560. btnGroupOptions: [],
  561. };
  562. },
  563. computed: {
  564. ...mapState({
  565. databaseName: (state) => state.user.dataSource.databaseName,
  566. databaseType: (state) => state.user.dataSource.databaseType,
  567. username: (state) => state.user.dataSource.username,
  568. tenantId: (state) => state.user.tenant.tenantId,
  569. tenantName: (state) => state.user.tenant.tenantName,
  570. }),
  571. // 数据字段
  572. dataArr() {
  573. return this.tableFieldList.filter((item) => item.isShow);
  574. },
  575. // 数据统计字段表名中文
  576. isShowTableName() {
  577. let tableComment;
  578. this.tableList.forEach((item) => {
  579. if (item.tableName === this.tableName) {
  580. tableComment = item.tableComment;
  581. }
  582. });
  583. return tableComment;
  584. },
  585. ...mapState({
  586. databaseName: (state) => state.user.dataSource.databaseName,
  587. databaseType: (state) => state.user.dataSource.databaseType,
  588. username: (state) => state.user.dataSource.username,
  589. tenantId: (state) => state.user.tenant.tenantId,
  590. tenantCode: (state) => state.user.tenant.tenantCode,
  591. }),
  592. searchArr() {
  593. if (!this.templateInfo?.where?.length) return [];
  594. return this.templateInfo.where.map((item) => item.conditionName);
  595. },
  596. checkedWhere() {
  597. if (!this.templateInfo?.where?.length) return [];
  598. return this.templateInfo?.where.filter((item) => {
  599. return this.checkedList.includes(item.conditionName);
  600. });
  601. },
  602. orderByFieldList() {
  603. // return this.tableFieldList.filter((item) => item.isShow);
  604. return this.tableFieldList;
  605. },
  606. primaryKeyList() {
  607. return this.tableFieldList.filter((item) => !item.isChildren);
  608. },
  609. // menuOrderNum() {
  610. // if (!this.formData.routePath) return 0;
  611. // let targetMenu = this.getTargetMenu(this.menus);
  612. // if (targetMenu.children?.length) {
  613. // return targetMenu.children.length;
  614. // } else {
  615. // return 0;
  616. // }
  617. // },
  618. },
  619. watch: {
  620. tableName: function (val) {
  621. if (val) {
  622. this.relationTableList = this.tableList
  623. .filter((item) => item.tableName != val)
  624. .map((item) => {
  625. return {
  626. tableName: item.tableName,
  627. tableComment: item.tableComment,
  628. };
  629. });
  630. } else {
  631. this.relationTableList = [];
  632. }
  633. },
  634. "formData.routePath"(nval, oval) {
  635. let targetMenu = this.getTargetMenu(this.menus);
  636. this.menuOrderNum = targetMenu.children ? targetMenu.children.length : 0;
  637. },
  638. },
  639. methods: {
  640. // 获取按钮模板数据
  641. async getBtnTemplate() {
  642. try {
  643. let res = await getDicts("drag_table_btn_template");
  644. if (res.code == 200) {
  645. this.btnTemplate = res.data.map((item) => JSON.parse(item.remark));
  646. } else {
  647. this.$message.error("获取按钮模板数据失败");
  648. }
  649. } catch (error) {
  650. this.$message.error("获取按钮模板数据失败");
  651. }
  652. },
  653. // 格式化按钮模板数据
  654. formateBtnTemplate() {
  655. let res = {
  656. hasPermiName: "",
  657. dragTableBtnTop: {},
  658. dragTableBtnRight: {},
  659. };
  660. let moduleName = this.menuList.find(
  661. (item) => item.menuId == this.formData.routePath
  662. )?.menuName;
  663. res.hasPermiName =
  664. this.tenantCode + ":" + moduleName + ":" + this.formData.menuName;
  665. let primaryKey = this.tableName + "." + this.formData.primaryKey;
  666. this.btnTemplate.forEach((item) => {
  667. item.children.forEach((i) => {
  668. if (i.btnType == "UPDATE" || i.btnType == "DELETE") {
  669. let btnParams = { commonFieldData: [], conditionData: [] };
  670. btnParams.conditionData.push({
  671. fieldName: primaryKey,
  672. fieldValue: "",
  673. });
  674. i.btnParams = JSON.stringify(btnParams);
  675. }
  676. });
  677. });
  678. res.dragTableBtnTop = this.btnTemplate[0];
  679. res.dragTableBtnRight = this.btnTemplate[1];
  680. return res;
  681. },
  682. // 字段描述验证规则
  683. // blurval(value){
  684. // this.isInputInvalid = value == '' ? true : false;
  685. // },
  686. // 树形控件change回调
  687. treeSelectChange(val) {
  688. // await this.getMenuList();
  689. //没有change事件
  690. },
  691. async treeSelectFocus() {
  692. await this.getMenuList();
  693. },
  694. // 获取目标menu
  695. getTargetMenu(menus) {
  696. for (let i = 0; i < menus.length; i++) {
  697. if (menus[i].menuId == this.formData.routePath) {
  698. return menus[i];
  699. } else if (menus.children?.length) {
  700. return this.getTargetMenu(menus.children);
  701. }
  702. }
  703. return false;
  704. },
  705. /** 查询列表 */
  706. getList(queryParams) {
  707. if (!this.tableName) return;
  708. let data = {
  709. databaseName: this.databaseName,
  710. databaseType: this.databaseType,
  711. tableName: this.tableName,
  712. };
  713. let tableComment = this.getTableCommont(this.tableName, this.tableList);
  714. // 获取当前表单结构信息
  715. getListName(data).then((res) => {
  716. this.tableFieldList = res.map((item, index) => {
  717. return {
  718. id: this.tableName + "_" + item.fieldName,
  719. fieldName: item.fieldName,
  720. fieldDescription: item.fieldDescription,
  721. relationTable: "",
  722. relationFieldName: "",
  723. relaFieldNameList: [],
  724. disableRelaFieldName: false,
  725. relationType: "",
  726. relationShowField: [],
  727. relationShowFiledList: [],
  728. disableRelaType: false,
  729. isShow: true,
  730. isSearch: false,
  731. isExport: false,
  732. relationTableList: this.relationTableList,
  733. tableName: this.tableName,
  734. tableComment,
  735. relationFieldList: [],
  736. };
  737. });
  738. });
  739. },
  740. // 关联表变化回调
  741. async ralationTableChange(row) {
  742. this.tableFieldList = this.tableFieldList.filter((item) => {
  743. return !row.relationFieldList.some((val) => {
  744. return val.id == item.id;
  745. });
  746. });
  747. row.relationFieldName = "";
  748. row.relationType = "";
  749. row.disableRelaFieldName = false;
  750. row.disableRelaType = false;
  751. row.relationFieldList = [];
  752. if (!row.relationTable) {
  753. return;
  754. }
  755. // 获取关联表的字段
  756. let data = {
  757. databaseName: this.databaseName,
  758. databaseType: this.databaseType,
  759. tableName: row.relationTable,
  760. };
  761. let tableComment = this.getTableCommont(
  762. row.relationTable,
  763. this.tableList
  764. );
  765. let res = await getListName(data);
  766. // 关联字段下拉列表数据
  767. row.relaFieldNameList = res.map((item) => {
  768. return {
  769. fieldName: item.fieldName,
  770. fieldDescription: item.fieldDescription,
  771. };
  772. });
  773. let relationTableList = row.relationTableList.filter(
  774. (item) => row.relationTable != item.tableName
  775. );
  776. row.relationFieldList = row.relaFieldNameList.map((item, index) => {
  777. return {
  778. id: row.relationTable + "_" + item.fieldName,
  779. fieldName: item.fieldName,
  780. fieldDescription: item.fieldDescription,
  781. relationTable: "",
  782. relationFieldName: "",
  783. relaFieldNameList: [],
  784. disableRelaFieldName: false,
  785. relationType: "",
  786. relationShowField: [],
  787. relationShowFiledList: [],
  788. disableRelaType: false,
  789. isShow: true,
  790. isSearch: false,
  791. isExport: false,
  792. relationTableList,
  793. tableName: row.relationTable,
  794. tableComment,
  795. relationFieldList: [],
  796. isChildren: true,
  797. };
  798. });
  799. // this.tableFieldList = [...this.tableFieldList, ...row.relationFieldList];
  800. row.disableRelaFieldName = true;
  801. },
  802. // 关联类型变化回调
  803. relationTypeChangeHandler(row) {
  804. let tempRelationFieldList = row.relationFieldList.filter((item) => {
  805. return !this.tableFieldList.find((val) => val.id === item.id);
  806. });
  807. this.tableFieldList = [...this.tableFieldList, ...tempRelationFieldList];
  808. },
  809. // 关联字段回调
  810. relationFieldChange(row) {
  811. if (!row.relationFieldName) {
  812. row.relationType = "";
  813. row.disableRelaType = false;
  814. row.relaFieldNameList = [];
  815. return;
  816. }
  817. row.disableRelaType = true;
  818. },
  819. // 勾选框回调
  820. handleSelectionChange(selection) {
  821. this.ids = selection.map((item) => item.id);
  822. // this.single = selection.length != 1;
  823. this.multiple = !selection.length;
  824. },
  825. // 分页查询
  826. pageList(row) {
  827. this.$refs.mychild.pageList(
  828. row == undefined
  829. ? {
  830. limit: this.queryParams.pageSize,
  831. page: this.queryParams.pageNum,
  832. }
  833. : row
  834. );
  835. },
  836. // 多选框改变回调
  837. checkedChangeHandle(val) {
  838. },
  839. // 获取所有表格
  840. async getAllTable() {
  841. let data = {
  842. databaseName: this.databaseName,
  843. databaseType: this.databaseType,
  844. };
  845. let res = await getFormName(data);
  846. const baseTable = await this.getDicts("base_table");
  847. this.tableList = res.data.filter((item) => {
  848. return !baseTable.data.some(
  849. (value) =>
  850. value.dictValue.toLowerCase() == item.tableName.toLowerCase()
  851. );
  852. });
  853. },
  854. //处理表格行拖拽
  855. initDragTable() {
  856. const el = this.$refs.dragTable.$el.querySelectorAll(
  857. ".el-table__body-wrapper > table > tbody"
  858. )[0];
  859. const sortable = Sortable.create(el, {
  860. handle: ".allowDrag",
  861. onEnd: (evt) => {
  862. const targetRow = this.tableFieldList.splice(evt.oldIndex, 1)[0];
  863. this.tableFieldList.splice(evt.newIndex, 0, targetRow);
  864. for (let index in this.tableFieldList) {
  865. this.tableFieldList[index].sort = parseInt(index) + 1;
  866. }
  867. },
  868. });
  869. },
  870. /** 转换菜单数据结构 */
  871. normalizer(node) {
  872. if (node.children && !node.children.length) {
  873. delete node.children;
  874. }
  875. return {
  876. id: node.menuId,
  877. label: node.menuName,
  878. children: node.children,
  879. };
  880. },
  881. // 获取路由表单数据
  882. async getMenuList() {
  883. let res = await getMenuList();
  884. if (res.code == 200) {
  885. this.menuList = res.data;
  886. this.menus = this.handleTree(res.data, "menuId");
  887. }
  888. },
  889. // 校验字段合法性(递归版)
  890. validateField(tableFieldList, validateParams) {
  891. if (!tableFieldList.length) {
  892. return;
  893. }
  894. for (let i = 0; i < tableFieldList.length; i++) {
  895. let temp = tableFieldList[i];
  896. if (!temp.fieldDescription?.trim() && temp.isShow) {
  897. //描述字段不能为空
  898. validateParams.isFieldDescrib = true;
  899. }
  900. if (
  901. temp.relationTable &&
  902. (!temp.relationFieldName || !temp.relationType)
  903. ) {
  904. // 关联条件不足
  905. validateParams.isRelationFieldAll = true;
  906. }
  907. if (temp.relationFieldList.length) {
  908. this.validateField(temp.relationFieldList, validateParams);
  909. }
  910. }
  911. },
  912. // 校验字段合法性(非递归版)
  913. validateTableData(tableFieldList) {
  914. if (!tableFieldList.length) {
  915. return {
  916. val: false,
  917. meg: "字段个数不能为空",
  918. };
  919. }
  920. for (let i = 0; i < tableFieldList.length; i++) {
  921. let temp = tableFieldList[i];
  922. if (!temp.fieldDescription?.trim() && temp.isShow) {
  923. return {
  924. val: false,
  925. msg: "显示的字段,字段描述不能为空",
  926. };
  927. }
  928. if (
  929. temp.relationTable &&
  930. (!temp.relationFieldName || !temp.relationType)
  931. ) {
  932. return {
  933. val: false,
  934. msg: "关联条件不足,请完善关联条件",
  935. };
  936. }
  937. }
  938. if (tableFieldList.filter((item) => item.isShow).length == 0) {
  939. return {
  940. val: false,
  941. msg: "显示的字段数不能为空",
  942. };
  943. }
  944. if (
  945. tableFieldList.filter((item) => item.isExport && !item.isChildren)
  946. .length == 0
  947. ) {
  948. return {
  949. val: false,
  950. msg: "导出字段中至少应有一个主表字段",
  951. };
  952. }
  953. // 导出的字段的描述不能重复
  954. let describeArr = tableFieldList
  955. .filter((item) => item.isExport)
  956. .map((item) => item.fieldDescription);
  957. let isRepeat = false;
  958. describeArr.map((item) => {
  959. if (describeArr.indexOf(item) != describeArr.lastIndexOf(item)) {
  960. isRepeat = true;
  961. }
  962. });
  963. if (isRepeat) {
  964. return {
  965. val: false,
  966. msg: "导出的字段描述不能重复",
  967. };
  968. }
  969. return {
  970. val: true,
  971. msg: "",
  972. };
  973. },
  974. // 递归拼接查询语句
  975. getSQLString(tableFieldList, fieldArr, tableArr, sqlType = "mysql") {
  976. let prefix = "{DBNAME}.";
  977. let asOrSpace = sqlType == "oracle" ? " " : " AS ";
  978. for (let i = 0; i < tableFieldList.length; i++) {
  979. let temp = tableFieldList[i];
  980. // if (temp.isShow) {
  981. let tempArr = prefix + temp.tableName + "." + temp.fieldName;
  982. // 给主表也加上表名前缀
  983. // if (temp.isChildren) {
  984. tempArr += asOrSpace + temp.tableName + "_" + temp.fieldName;
  985. // }
  986. fieldArr.push(tempArr);
  987. // }
  988. if (temp.relationTable && temp.relationFieldName && temp.relationType) {
  989. // fieldArr.push(temp.relationTable + "." + temp.relationFieldName);
  990. let isNeedUsername = sqlType == "oracle" ? this.username + "." : "";
  991. tableArr.push(
  992. temp.relationType +
  993. " " +
  994. isNeedUsername +
  995. prefix +
  996. temp.relationTable +
  997. asOrSpace +
  998. temp.relationTable +
  999. " ON " +
  1000. prefix +
  1001. temp.relationTable +
  1002. "." +
  1003. temp.relationFieldName +
  1004. " = " +
  1005. prefix +
  1006. temp.tableName +
  1007. "." +
  1008. temp.fieldName
  1009. );
  1010. }
  1011. // if (temp.relationFieldList.length) {
  1012. // this.getSQLString(temp.relationFieldList, fieldArr, tableArr);
  1013. // }
  1014. }
  1015. // 如果主键不包含显示,则添加至sql语句中
  1016. let isNotInclude = this.primaryKeyList.find(
  1017. (val) => !val.isShow && val.fieldName == this.formData.primaryKey
  1018. );
  1019. if (isNotInclude) {
  1020. fieldArr.push(
  1021. prefix +
  1022. isNotInclude.tableName +
  1023. "." +
  1024. isNotInclude.fieldName +
  1025. asOrSpace +
  1026. isNotInclude.tableName +
  1027. "_" +
  1028. isNotInclude.fieldName
  1029. );
  1030. }
  1031. },
  1032. // 拼接查询sql语句
  1033. getSQLStr() {
  1034. let classificationData =
  1035. this.$refs.classificationQueryRef.getConditions(); //按钮中的查询条件
  1036. let prefix = "{DBNAME}.";
  1037. let sqlType = this.databaseType; //数据库类型
  1038. let asOrSpace = sqlType == "oracle" ? " " : " AS ";
  1039. // let sqlType = "oracle";
  1040. let sql = "";
  1041. // mysql
  1042. sql += "SELECT ";
  1043. let fieldNameArr = [],
  1044. relaTypeArr = [];
  1045. this.getSQLString(
  1046. this.tableFieldList,
  1047. fieldNameArr,
  1048. relaTypeArr,
  1049. sqlType
  1050. );
  1051. // if (discriISEmpty) {
  1052. // return false;
  1053. // }
  1054. let styleFieldList = this.$refs.styleTableRef.getStyleTableField();
  1055. styleFieldList = styleFieldList.map((item) => {
  1056. return `${prefix}${item}${asOrSpace}${item.replace(".", "_")}`;
  1057. });
  1058. let set = new Set([...fieldNameArr, ...styleFieldList]);
  1059. fieldNameArr = [...set];
  1060. let isNeedUsername = sqlType == "oracle" ? this.username + "." : "";
  1061. sql +=
  1062. fieldNameArr.join(",") +
  1063. " FROM " +
  1064. isNeedUsername +
  1065. prefix +
  1066. this.tableName +
  1067. asOrSpace +
  1068. this.tableName;
  1069. if (relaTypeArr.length) {
  1070. sql += " " + relaTypeArr.join(" ");
  1071. }
  1072. return sql;
  1073. },
  1074. getStaticSQL(key) {
  1075. let prefix = "{DBNAME}.";
  1076. let sqlType = this.databaseType; //数据库类型
  1077. // let sqlType = "oracle";
  1078. let sql = "";
  1079. // mysql
  1080. sql += "SELECT ";
  1081. let fieldNameArr = [],
  1082. relaTypeArr = [];
  1083. this.getSQLString(
  1084. this.tableFieldList,
  1085. fieldNameArr,
  1086. relaTypeArr,
  1087. sqlType
  1088. );
  1089. let isNeedUsername = sqlType == "oracle" ? this.username + "." : "";
  1090. let asOrSpace = sqlType == "oracle" ? " " : " AS ";
  1091. sql +=
  1092. key +
  1093. " FROM " +
  1094. isNeedUsername +
  1095. prefix +
  1096. this.tableName +
  1097. asOrSpace +
  1098. this.tableName;
  1099. if (relaTypeArr.length) {
  1100. sql += " " + relaTypeArr.join(" ");
  1101. }
  1102. return sql;
  1103. },
  1104. // 处理列表信息
  1105. columnsHandler(columns) {
  1106. let resArr = [];
  1107. columns.forEach((item) => {
  1108. for (const key in item) {
  1109. let tempObj = {};
  1110. tempObj.key = key;
  1111. tempObj.value = item[key];
  1112. resArr.push(tempObj);
  1113. }
  1114. });
  1115. return resArr;
  1116. },
  1117. // 下划线命名转驼峰命名
  1118. toUpperCase(str) {
  1119. let nstr = str.replace(/(?:_)+([^_])/g, function ($0, $1) {
  1120. return $1.toUpperCase();
  1121. });
  1122. nstr = nstr.replace(nstr[0], nstr[0].toLowerCase());
  1123. return nstr;
  1124. },
  1125. // 递归获取列表信息
  1126. getCol(
  1127. tableFieldList,
  1128. columns,
  1129. searchFieldList = [],
  1130. tableExportField = {},
  1131. allColumns = []
  1132. ) {
  1133. if (!tableFieldList.length) return;
  1134. for (let i = 0; i < tableFieldList.length; i++) {
  1135. let temp = tableFieldList[i];
  1136. let tempFieldName = "",
  1137. exportFieldName = "";
  1138. if (temp.isChildren) {
  1139. tempFieldName = temp.tableName + "_" + temp.fieldName;
  1140. exportFieldName = temp.tableName + "@" + temp.fieldName;
  1141. } else {
  1142. // tempFieldName = temp.fieldName;
  1143. tempFieldName = temp.tableName + "_" + temp.fieldName;
  1144. exportFieldName = temp.tableName + "_" + temp.fieldName;
  1145. }
  1146. // 保存所有字段
  1147. let obj = {
  1148. tableName: temp.tableName,
  1149. fieldName: temp.fieldName,
  1150. fieldDescription: temp.fieldDescription,
  1151. };
  1152. allColumns.push(obj);
  1153. if (temp.isShow) {
  1154. let tempObj = {};
  1155. tempObj[tempFieldName] = temp.fieldDescription;
  1156. columns.push(tempObj);
  1157. }
  1158. if (temp.isSearch) {
  1159. searchFieldList.push(temp.tableName + "." + temp.fieldName);
  1160. }
  1161. if (temp.isExport) {
  1162. tableExportField[exportFieldName] = temp.fieldDescription;
  1163. }
  1164. }
  1165. },
  1166. // 更新路由
  1167. reloadRouter() {
  1168. this.$store.dispatch("GenerateRoutes").then((accessRoutes) => {
  1169. this.$router.addRoutes(accessRoutes); // 动态添加可访问路由表
  1170. });
  1171. },
  1172. // 预览结果回调
  1173. async previewHandle() {
  1174. this.$refs.formData.validate(async (valid) => {
  1175. // 至少包含一个查询项
  1176. if (valid) {
  1177. if (!this.tableName) {
  1178. this.$message.error("请选择数据表");
  1179. return;
  1180. }
  1181. // 检验表单合法性
  1182. let validRes = this.validateTableData(this.tableFieldList);
  1183. if (!validRes.val) {
  1184. this.$message.error(validRes.msg);
  1185. return;
  1186. }
  1187. // let validateParams = {
  1188. // isFieldDescrib: false,
  1189. // isRelationFieldAll: false,
  1190. // };
  1191. // this.validateField(this.tableFieldList, validateParams);
  1192. // if (validateParams.isFieldDescrib) {
  1193. // this.$message.error("需要显示的字段描述不能为空");
  1194. // return;
  1195. // }
  1196. // if (validateParams.isRelationFieldAll) {
  1197. // this.$message.error("请补全关联条件");
  1198. // return;
  1199. // }
  1200. // 拼接预览的sql查询语句
  1201. this.queryParams.basicMap.sql = this.getSQLStr();
  1202. // 获取表头信息
  1203. let tempColumns = [],
  1204. searchFieldList = [];
  1205. this.getCol(this.tableFieldList, tempColumns, searchFieldList);
  1206. if (!searchFieldList.length) {
  1207. this.$message.warning("请至少选择一个包含查询字段");
  1208. return false;
  1209. }
  1210. const dataType = await this.getDicts("table_statistic_type");
  1211. this.deepragTableStatisticList = JSON.parse(
  1212. JSON.stringify(this.dragTableStatisticList)
  1213. );
  1214. this.deepragTableStatisticList.forEach((item) => {
  1215. dataType.data.forEach((val) => {
  1216. if (val.dictValue == item.statisticType) {
  1217. item.statisticType = val.dictLabel;
  1218. }
  1219. });
  1220. });
  1221. this.columns = this.columnsHandler(tempColumns);
  1222. this.queryParams.isAsc = this.formData.isAsc;
  1223. // 发送请求获取预览数据
  1224. let res = await dragTablePreview(this.queryParams);
  1225. this.tableDataList = [];
  1226. res.rows.forEach((item) => {
  1227. this.tableDataList.push(item.resultMap);
  1228. });
  1229. this.total = res.total;
  1230. this.isShowPreview = true;
  1231. } else {
  1232. this.$message.warning("请完善表单");
  1233. return false;
  1234. }
  1235. });
  1236. // this.$refs.tableForm.validate((val) => {
  1237. // if (val) {
  1238. // } else {
  1239. // this.$message.warning("请完善表单");
  1240. // return false;
  1241. // }
  1242. // });
  1243. },
  1244. // 联合查询条件
  1245. getBtnData(searchArr = []) {
  1246. // 按钮条件list
  1247. let classificationData = this.$refs.classificationQueryRef.getConditions();
  1248. let res = searchArr.map((item) => {
  1249. return {
  1250. conditionField: item,
  1251. conditionName: "超级查询",
  1252. conditionType: "SuperQuery",
  1253. };
  1254. })
  1255. let list = Object.assign([], classificationData);
  1256. // 校验 填写不完整删除查询条件
  1257. classificationData.forEach((item, index) => {
  1258. if (item.conditionName == "" || item.conditionField == "" || item.conditionType == "") {
  1259. classificationData.splice(index, 1);
  1260. }
  1261. })
  1262. return [...res, ...classificationData];
  1263. },
  1264. // 创建回调
  1265. async createHandle() {
  1266. // this.sqlKey = uuidv4();
  1267. this.isInputInvalid = true;
  1268. this.$refs.formData.validate(async (valid) => {
  1269. if (valid) {
  1270. // 检验表单合法性
  1271. let validRes = this.validateTableData(this.tableFieldList);
  1272. if (!validRes.val) {
  1273. this.$message.error(validRes.msg);
  1274. return;
  1275. }
  1276. let columns = [],
  1277. searchFieldList = [],
  1278. tableExportField = {},
  1279. allColumns = [];
  1280. this.getCol(
  1281. this.tableFieldList,
  1282. columns,
  1283. searchFieldList,
  1284. tableExportField,
  1285. allColumns
  1286. );
  1287. let echoData = {
  1288. tableName: this.tableName,
  1289. tableFieldData: this.tableFieldList,
  1290. formData: this.formData,
  1291. filterData: encodeURIComponent(
  1292. this.$refs.dataFilterRef?.getEchoData()
  1293. ),
  1294. };
  1295. if (!searchFieldList.length) {
  1296. this.$message.warning("请至少选择一个包含查询字段");
  1297. return false;
  1298. }
  1299. this.uuid = uuidv4();
  1300. this.tableKey = uuidv4();
  1301. // 表单
  1302. let result;
  1303. let btnTemplate = this.formateBtnTemplate();
  1304. // 如果是表格菜单,处理菜单路由和菜单名称
  1305. if (this.formData.dtType === '0') {
  1306. // if (this.tId && this.menuId) {
  1307. if (this.menuId && !this.isNeedNewMenu) {
  1308. //原菜单存在,仅需要修改菜单数据
  1309. let payLoad = {
  1310. menuId: this.menuId,
  1311. menuName: this.formData.menuName,
  1312. parentId: this.formData.routePath,
  1313. component: "tablelist/commonTable/listInfo",
  1314. icon: "",
  1315. isCache: "0",
  1316. isFrame: "1",
  1317. menuType: "C",
  1318. orderNum: this.menuOrderNum,
  1319. status: "0",
  1320. visible: "0",
  1321. tenantId: this.tenantId,
  1322. };
  1323. result = await updateMenu(payLoad);
  1324. } else if (this.isNeedNewMenu) {
  1325. //给旧表格新增菜单
  1326. let tableKeyObj = {
  1327. tableKey: this.editData.tableKey,
  1328. };
  1329. let payLoad = {
  1330. component: "tablelist/commonTable/listInfo",
  1331. icon: "",
  1332. isCache: "0",
  1333. isFrame: "1",
  1334. menuName: this.formData.menuName,
  1335. // menuId: this.formData.routePath,
  1336. menuType: "C",
  1337. orderNum: this.menuOrderNum,
  1338. parentId: this.formData.routePath,
  1339. path: this.uuid,
  1340. query: JSON.stringify(tableKeyObj),
  1341. status: "0",
  1342. visible: "0",
  1343. tenantId: this.tenantId,
  1344. };
  1345. result = await addMenu(payLoad);
  1346. } else {
  1347. let tableKeyObj = {
  1348. tableKey: this.uuid,
  1349. };
  1350. let payLoad = {
  1351. component: "tablelist/commonTable/listInfo",
  1352. icon: "",
  1353. isCache: "0",
  1354. isFrame: "1",
  1355. menuName: this.formData.menuName,
  1356. // menuId: this.formData.routePath,
  1357. menuType: "C",
  1358. orderNum: this.menuOrderNum,
  1359. parentId: this.formData.routePath,
  1360. path: this.uuid,
  1361. query: JSON.stringify(tableKeyObj),
  1362. status: "0",
  1363. visible: "0",
  1364. tenantId: this.tenantId,
  1365. };
  1366. result = await addMenu(payLoad);
  1367. }
  1368. if (result.code == 200) {
  1369. // 更新路由
  1370. this.reloadRouter();
  1371. } else {
  1372. this.$message.warning(result.msg);
  1373. return;
  1374. }
  1375. }
  1376. // 得到联合查询数组
  1377. let conditions = this.$refs.dataFilterRef?.getConditions();
  1378. let conditionDefaultValueMap = {};
  1379. conditions.map((item) => {
  1380. conditionDefaultValueMap[item.fieldName] = encodeURIComponent(
  1381. " " + item.condition + " " + item.refValue
  1382. );
  1383. });
  1384. // let isAsc = this.formData.isAsc == "ASC" ? 0 : 1;
  1385. let btnData = this.getBtnData(searchFieldList);
  1386. let data = {
  1387. conditionDefaultValueMap, //数据筛选条件
  1388. tId: this.tId,
  1389. dtName: this.formData.menuName,
  1390. menuId: this.formData.routePath,
  1391. tableKey: this.uuid, // 暂定
  1392. dtTableName: this.tableName,
  1393. primaryKey: this.formData.primaryKey,
  1394. orderByColumn: this.formData.orderByColumn,
  1395. sortOrder: this.formData.isAsc,
  1396. sqlKey: this.tableKey,
  1397. dtColumnName: columns, //列字段标题名称(存储显示字段信息
  1398. timeFormat: this.formData.timeFormate,
  1399. // searchFieldList: searchFieldList, //搜索字段数组
  1400. searchFieldList: btnData, //搜索字段数组
  1401. tableSql: this.getSQLStr(), // 暂定
  1402. tableExportField: tableExportField, //导出字段名及列名
  1403. echoData: JSON.stringify(echoData),
  1404. spare: JSON.stringify(allColumns), //所有字段数据
  1405. // 样式相关数据
  1406. dragTableStyleList: this.$refs.styleTableRef.getSaveData(),
  1407. dtType: this.formData.dtType
  1408. };
  1409. this.dragTableStatisticList.forEach((item) => {
  1410. if (this.tId == "") {
  1411. item.tableKey = this.tableKey;
  1412. this.staticSqlKey = uuidv4();
  1413. item.sqlKey = this.staticSqlKey;
  1414. }
  1415. if (item.sqlKey) {
  1416. this.staticSqlKey = item.sqlKey;
  1417. } else {
  1418. this.staticSqlKey = uuidv4();
  1419. item.sqlKey = this.staticSqlKey;
  1420. }
  1421. let key =
  1422. item.statisticType +
  1423. "(" +
  1424. item.statisticField +
  1425. ")" +
  1426. " as result";
  1427. this.tableSqlList.push({
  1428. tableSql: this.getStaticSQL(key),
  1429. sqlKey: this.staticSqlKey,
  1430. });
  1431. this.searchFieldList = searchFieldList;
  1432. });
  1433. let res;
  1434. let res1;
  1435. // if (this.tId && this.menuId) {
  1436. if (this.tId) {
  1437. data.menuId = this.menuId || result?.data;
  1438. data.sqlKey = this.editData.sqlKey;
  1439. data.tableKey = this.editData.tableKey;
  1440. data.dragTableBtnRelevanceList = this.getBtnMapList(
  1441. data.tableKey
  1442. );
  1443. res = await editTable(data);
  1444. this.dragTableStatisticList.forEach((item) => {
  1445. item.tableKey = this.editData.tableKey;
  1446. });
  1447. res1 = await updateStatistic({
  1448. tableKey: this.editData.tableKey,
  1449. dragTableStatisticList: this.dragTableStatisticList,
  1450. tableSqlList: this.tableSqlList,
  1451. searchFieldList: this.searchFieldList,
  1452. dtTableName: this.tableName,
  1453. });
  1454. } else {
  1455. data.menuId = result?.data;
  1456. data.dragTableBtnRelevanceList = this.getBtnMapList(
  1457. data.tableKey
  1458. );
  1459. res = await addDragTable(data);
  1460. this.dragTableStatisticList.forEach((item) => {
  1461. item.tableKey = this.uuid;
  1462. });
  1463. if (res.code == 200 && this.dragTableStatisticList.length !== 0) {
  1464. // 新增统计数据
  1465. res1 = addStatistic({
  1466. tableKey: this.uuid,
  1467. dragTableStatisticList: this.dragTableStatisticList,
  1468. tableSqlList: this.tableSqlList,
  1469. searchFieldList: this.searchFieldList,
  1470. dtTableName: this.tableName,
  1471. });
  1472. }
  1473. if (res.code == 200) {
  1474. // 新增默认按钮数据
  1475. btnTemplate.tableKey = data.tableKey;
  1476. btnTemplate.menuID = result?.data;
  1477. let btnRes = await insertByDefaultBtn(btnTemplate);
  1478. if (btnRes.code == 200) {
  1479. // 发送权限请求
  1480. let payload = btnRes.data.map((item) => {
  1481. return {
  1482. ...item,
  1483. tenantId: this.tenantId,
  1484. };
  1485. });
  1486. let btnPowerRes = await tbnHasPerms(payload);
  1487. } else {
  1488. console.error(btnRes);
  1489. }
  1490. }
  1491. }
  1492. // 关闭当前页面
  1493. if (this.tId) {
  1494. // console.log('---------------------修改成功',res)
  1495. if (res.code == 200) {
  1496. this.$message.success("修改成功");
  1497. this.$tab.closePage();
  1498. this.$router.push({
  1499. path: "/system/fromModel/index/tablelist",
  1500. });
  1501. } else {
  1502. this.$message.warning("修改失败");
  1503. }
  1504. } else {
  1505. // console.log('---------------------创建成功',res)
  1506. if (res.code == 200) {
  1507. this.$message.success("创建成功");
  1508. this.$tab.closePage();
  1509. // console.log('---------------------创建成功-----------------------------')
  1510. this.$router.push({
  1511. path: "/system/fromModel/index/tablelist",
  1512. });
  1513. } else {
  1514. this.$message.warning("创建失败");
  1515. }
  1516. this.isShowPreview = false;
  1517. }
  1518. // }
  1519. // else {
  1520. // this.$message.warning(result.msg);
  1521. // return;
  1522. // }
  1523. } else {
  1524. this.$message.warning("请完善表单");
  1525. return false;
  1526. }
  1527. });
  1528. },
  1529. getBtnMapList(tableKey) {
  1530. let res = this.formData.btnGroupList.map((item) => {
  1531. return {
  1532. tableKey,
  1533. btnKey: item,
  1534. };
  1535. });
  1536. return res;
  1537. },
  1538. // 修改表格回显数据
  1539. async initTableData(tId) {
  1540. let res = await getTableInfo(tId);
  1541. if (res.code == 200) {
  1542. this.dragTableStatisticList = res?.data?.dragTableStatisticList;
  1543. this.dragTableStyleList = res?.data?.dragTableStyleList;
  1544. let echoData = JSON.parse(res?.data?.echoData);
  1545. this.tableName = echoData?.tableName;
  1546. this.tableFieldList = echoData?.tableFieldData;
  1547. this.filterDataEcho = echoData?.filterData;
  1548. let {
  1549. isShowList,
  1550. timeFormate,
  1551. orderByColumn,
  1552. isAsc,
  1553. primaryKey,
  1554. menuName,
  1555. btnGroupList,
  1556. dtType
  1557. } = echoData.formData;
  1558. Object.assign(this.formData, {
  1559. menuName,
  1560. isShowList,
  1561. timeFormate,
  1562. orderByColumn,
  1563. isAsc,
  1564. primaryKey,
  1565. btnGroupList,
  1566. dtType
  1567. });
  1568. this.formData.routePath = this.getParentMenuId(
  1569. res.data.menuId,
  1570. this.menus,
  1571. {}
  1572. );
  1573. // console.log('[传的res.data.menuId和返回的值]', res.data.menuId,this.formData.routePath)
  1574. // console.log('[传的第二个值]',this.menus)
  1575. //处理联合查询回显数据
  1576. this.classificationDataEcho = res.data.searchFieldList.filter(
  1577. (item) => item.conditionType != "SuperQuery" && item.conditionType != "DefaultQuery"
  1578. );
  1579. if (!this.formData.routePath && this.formData.dtType != '3') {
  1580. this.$message.warning("该表格菜单路由已经删除,请重新配置");
  1581. this.isNeedNewMenu = true;
  1582. }
  1583. // this.formData.routePath
  1584. this.editData = res.data;
  1585. let val = await getParticMenu(res.data.tableKey);
  1586. if (val.code == 200) {
  1587. this.menuId = val.data?.menuId;
  1588. }
  1589. } else {
  1590. this.$message.error("数据回显失败");
  1591. }
  1592. },
  1593. // 获取父级menuId
  1594. getParentMenuId(menuId, menus) {
  1595. // console.log('[menuId]',menuId,menus)
  1596. let res;
  1597. for (let i = 0; i < menus.length; i++) {
  1598. let item = menus[i];
  1599. if (item.menuId == menuId) {
  1600. res = item.parentId;
  1601. break;
  1602. } else if (item.children?.length) {
  1603. if (
  1604. this.getParentMenuId(
  1605. menuId,
  1606. JSON.parse(JSON.stringify(item.children))
  1607. )
  1608. ) {
  1609. res = this.getParentMenuId(
  1610. menuId,
  1611. JSON.parse(JSON.stringify(item.children))
  1612. );
  1613. }
  1614. }
  1615. }
  1616. return res;
  1617. },
  1618. // tab切换
  1619. tabhandleClick() {
  1620. this.menudata = this.activeName != "menuedit" ? true : false;
  1621. },
  1622. // 添加数据字段对话框
  1623. async addDataDialog() {
  1624. this.staictitle = "添加统计数据字段";
  1625. this.isShowAddData = true;
  1626. this.dataType = await this.getDicts("table_statistic_type");
  1627. this.dataType = this.dataType.data;
  1628. },
  1629. async updataDialog() {
  1630. },
  1631. // 修改数据字段
  1632. upadtaData() {
  1633. this.dragTableStatisticList.forEach((item, index) => {
  1634. if (item.id !== undefined && item.id == this.dataCountFormData.id) {
  1635. this.dragTableStatisticList[index] = this.dataCountFormData;
  1636. } else if (item.xid == this.dataCountFormData.xid) {
  1637. this.dragTableStatisticList[index] = this.dataCountFormData;
  1638. }
  1639. });
  1640. this.isShowAddData = false;
  1641. this.dataCountFormData = {};
  1642. },
  1643. // 添加数据字段
  1644. addData() {
  1645. this.dataCountFormData.xid = Date.now();
  1646. this.dataCountFormData.tableKey = this.tableKey;
  1647. this.dragTableStatisticList.push(this.dataCountFormData);
  1648. this.isShowAddData = false;
  1649. this.dataCountFormData = {};
  1650. },
  1651. // 关闭添加数据字段
  1652. closeAddDialog() {
  1653. this.isShowAddData = false;
  1654. this.dataCountFormData = {};
  1655. },
  1656. // 确定统计
  1657. countHandle() {
  1658. addStatistic({
  1659. dragTableStatisticList: this.dragTableStatisticList,
  1660. tableSqlList: this.tableSqlList,
  1661. searchFieldList: this.searchFieldList,
  1662. dtTableName: this.tableName,
  1663. }).then((res) => {
  1664. });
  1665. },
  1666. // 修改统计信息
  1667. async handleUpdateStat(row) {
  1668. this.dataCountFormData = row;
  1669. this.dataCountFormData.xid = Date.now();
  1670. this.staictitle = "修改统计数据字段";
  1671. this.dataType = await this.getDicts("table_statistic_type");
  1672. this.dataType = this.dataType.data;
  1673. this.isShowAddData = true;
  1674. },
  1675. // 删除统计信息
  1676. handleDeleteStat(row) {
  1677. this.dragTableStatisticList.forEach((item, index) => {
  1678. if (item.id == row.id) {
  1679. this.dragTableStatisticList.splice(index, 1);
  1680. }
  1681. });
  1682. },
  1683. // 获取当前表描述
  1684. getTableCommont(tableName, tableList) {
  1685. return tableList.find((item) => item.tableName == tableName).tableComment;
  1686. },
  1687. // 获取按钮组数据
  1688. async getBtnList() {
  1689. let res = await listBtn({ isEnablePaging: false, btnParentId: 0 });
  1690. // this.btnGroupOption=res.rows
  1691. // this.btnGroupOptions = res.rows.filter((item) => !item.whetherBind);
  1692. this.btnGroupOptions = res.rows.filter((item) => {
  1693. let isMyBtn = this.formData.btnGroupList.some(
  1694. (val) => item.btnKey == val
  1695. );
  1696. return !item.whetherBind || isMyBtn;
  1697. });
  1698. },
  1699. },
  1700. created() {
  1701. },
  1702. async mounted() {
  1703. this.getBtnTemplate();
  1704. this.getAllTable();
  1705. this.initDragTable();
  1706. await this.getMenuList();
  1707. if (this.$route.query.tId) {
  1708. this.tId = this.$route.query.tId;
  1709. await this.initTableData(this.tId);
  1710. }
  1711. this.getBtnList();
  1712. },
  1713. };
  1714. </script>
  1715. <style scoped lang="scss">
  1716. .discribe {
  1717. display: block;
  1718. max-width: 200px;
  1719. white-space: nowrap;
  1720. overflow: hidden;
  1721. text-overflow: ellipsis;
  1722. }
  1723. .ipt {
  1724. height: 36px;
  1725. line-height: 36px;
  1726. font-size: 14px;
  1727. width: 100%;
  1728. outline: none;
  1729. text-align: center;
  1730. background-color: #fff;
  1731. border: 1px solid #dcdfe6;
  1732. color: #606266;
  1733. display: inline-block;
  1734. border-radius: 4px;
  1735. }
  1736. .isNullDesc {
  1737. border-color: #ff4949 !important;
  1738. }
  1739. .ipt:focus {
  1740. border-color: #1890ff;
  1741. }
  1742. ::v-deep .right_card {
  1743. min-height: 500px !important;
  1744. }
  1745. ::v-deep .vue-treeselect--has-value .vue-treeselect__input {
  1746. vertical-align: middle !important;
  1747. }
  1748. .menudata {
  1749. width: 70% !important;
  1750. }
  1751. .edit {
  1752. width: 30% !important;
  1753. }
  1754. .mb10 {
  1755. margin-top: 10px;
  1756. }
  1757. .cardBox {
  1758. display: flex;
  1759. align-content: space-between;
  1760. flex-wrap: wrap;
  1761. align-content: flex-start;
  1762. }
  1763. .card {
  1764. /* width:15%; */
  1765. height: 150px;
  1766. flex-basis: 15%;
  1767. margin-bottom: 10px;
  1768. margin-right: 15px;
  1769. min-width: 180px;
  1770. .title {
  1771. /* width:20%; */
  1772. font-size: 18px;
  1773. margin-bottom: 5px;
  1774. white-space: nowrap;
  1775. overflow: hidden;
  1776. text-overflow: ellipsis;
  1777. }
  1778. .description {
  1779. width: 70%;
  1780. font-size: 13px;
  1781. color: #9699a2;
  1782. overflow: hidden;
  1783. text-overflow: ellipsis;
  1784. display: -webkit-box;
  1785. -webkit-box-orient: vertical;
  1786. -webkit-line-clamp: 3;
  1787. word-break: break-all;
  1788. float: left;
  1789. }
  1790. .type {
  1791. float: right;
  1792. margin-top: 40px;
  1793. .statisticType {
  1794. font-size: 18px;
  1795. }
  1796. }
  1797. .count {
  1798. font-size: 25px;
  1799. }
  1800. }
  1801. .mb8 {
  1802. ::v-deep .el-col-18 {
  1803. width: 30% !important;
  1804. min-width: 220px !important;
  1805. }
  1806. ::v-deep .previewbtn {
  1807. min-width: 190px !important;
  1808. }
  1809. }
  1810. </style>