ソースを参照

Merge branch 'master' of http://git.cc-lotus.info/laboratory/ruoyi-project

guhongwei 3 年 前
コミット
32fc3f5551

+ 1 - 1
java代码/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java

@@ -40,7 +40,7 @@ public class SysDictDataController extends BaseController
     @Autowired
     @Autowired
     private ISysDictTypeService dictTypeService;
     private ISysDictTypeService dictTypeService;
 
 
-    @PreAuthorize("@ss.hasPermi('system:dict:list')")
+    // @PreAuthorize("@ss.hasPermi('system:dict:list')")
     @GetMapping("/list")
     @GetMapping("/list")
     public TableDataInfo list(SysDictData dictData)
     public TableDataInfo list(SysDictData dictData)
     {
     {

+ 5 - 5
java代码/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java

@@ -98,7 +98,7 @@ public class SysUserController extends BaseController
     /**
     /**
      * 根据用户编号获取详细信息
      * 根据用户编号获取详细信息
      */
      */
-    @PreAuthorize("@ss.hasPermi('system:user:query')")
+    // @PreAuthorize("@ss.hasPermi('system:user:query')")
     @GetMapping(value = { "/", "/{userId}" })
     @GetMapping(value = { "/", "/{userId}" })
     public AjaxResult getInfo(@PathVariable(value = "userId", required = false) String userId)
     public AjaxResult getInfo(@PathVariable(value = "userId", required = false) String userId)
     {
     {
@@ -118,7 +118,7 @@ public class SysUserController extends BaseController
     /**
     /**
      * 新增用户
      * 新增用户
      */
      */
-    @PreAuthorize("@ss.hasPermi('system:user:add')")
+    // @PreAuthorize("@ss.hasPermi('system:user:add')")
     @Log(title = "用户管理", businessType = BusinessType.INSERT)
     @Log(title = "用户管理", businessType = BusinessType.INSERT)
     @PostMapping
     @PostMapping
     public AjaxResult add(@Validated @RequestBody SysUser user)
     public AjaxResult add(@Validated @RequestBody SysUser user)
@@ -145,7 +145,7 @@ public class SysUserController extends BaseController
     /**
     /**
      * 修改用户
      * 修改用户
      */
      */
-    @PreAuthorize("@ss.hasPermi('system:user:edit')")
+    // @PreAuthorize("@ss.hasPermi('system:user:edit')")
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @PutMapping
     @PutMapping
     public AjaxResult edit(@Validated @RequestBody SysUser user)
     public AjaxResult edit(@Validated @RequestBody SysUser user)
@@ -168,7 +168,7 @@ public class SysUserController extends BaseController
     /**
     /**
      * 删除用户
      * 删除用户
      */
      */
-    @PreAuthorize("@ss.hasPermi('system:user:remove')")
+    // @PreAuthorize("@ss.hasPermi('system:user:remove')")
     @Log(title = "用户管理", businessType = BusinessType.DELETE)
     @Log(title = "用户管理", businessType = BusinessType.DELETE)
     @DeleteMapping("/{userIds}")
     @DeleteMapping("/{userIds}")
     public AjaxResult remove(@PathVariable String[] userIds)
     public AjaxResult remove(@PathVariable String[] userIds)
@@ -179,7 +179,7 @@ public class SysUserController extends BaseController
     /**
     /**
      * 重置密码
      * 重置密码
      */
      */
-    @PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
+    // @PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @PutMapping("/resetPwd")
     @PutMapping("/resetPwd")
     public AjaxResult resetPwd(@RequestBody SysUser user)
     public AjaxResult resetPwd(@RequestBody SysUser user)

+ 1 - 0
java代码/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java

@@ -111,6 +111,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
         .antMatchers("/*/api-docs").anonymous()
         .antMatchers("/*/api-docs").anonymous()
         .antMatchers("/druid/**").anonymous()
         .antMatchers("/druid/**").anonymous()
         .antMatchers("/wx/**").anonymous()
         .antMatchers("/wx/**").anonymous()
+        .antMatchers("/user/**").anonymous()
         // 除上面外的所有请求全部需要鉴权认证
         // 除上面外的所有请求全部需要鉴权认证
         .anyRequest().authenticated()
         .anyRequest().authenticated()
         // .anyRequest().permitAll()
         // .anyRequest().permitAll()

+ 5 - 1
java代码/ruoyi-ui/package.json

@@ -59,7 +59,9 @@
     "vue-cropper": "0.5.5",
     "vue-cropper": "0.5.5",
     "vue-router": "3.4.9",
     "vue-router": "3.4.9",
     "vuedraggable": "2.24.3",
     "vuedraggable": "2.24.3",
-    "vuex": "3.6.0"
+    "vuex": "3.6.0",
+    "x-data-spreadsheet": "^1.1.9",
+    "xlsx": "^0.17.5"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@vue/cli-plugin-babel": "4.4.6",
     "@vue/cli-plugin-babel": "4.4.6",
@@ -74,6 +76,8 @@
     "runjs": "4.4.2",
     "runjs": "4.4.2",
     "sass": "1.32.0",
     "sass": "1.32.0",
     "sass-loader": "10.1.0",
     "sass-loader": "10.1.0",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
     "script-ext-html-webpack-plugin": "2.1.5",
     "script-ext-html-webpack-plugin": "2.1.5",
     "svg-sprite-loader": "5.1.1",
     "svg-sprite-loader": "5.1.1",
     "vue-template-compiler": "2.6.12"
     "vue-template-compiler": "2.6.12"

+ 78 - 0
java代码/ruoyi-ui/src/components/free/excel-view.vue

@@ -0,0 +1,78 @@
+<template>
+  <div id="excel-view">
+    <el-row type="flex" :gutter="20">
+      <el-col :span="12">
+        <div id="excel" />
+      </el-col>
+      <el-col :span="12">
+        extInfo
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import zhCN from 'x-data-spreadsheet/src/locale/zh-cn'
+import Spreadsheet from 'x-data-spreadsheet'
+Spreadsheet.locale('zh-cn', zhCN)
+const _ = require('lodash')
+export default {
+  name: 'ExcelView',
+  components: {},
+  model: {
+    prop: 'data',
+    event: 'change'
+  },
+  props: {
+    data: { type: Object, required: true },
+    view: { type: Object }
+  },
+  data: function() {
+    return {
+      sheet: undefined,
+      excelOptions: {
+        mode: 'edit',
+        view: this.view,
+        style: {
+          align: 'center',
+          valign: 'middle',
+          border: {
+            bottom: ['thick', '#000'],
+            top: ['thick', '#000'],
+            left: ['thick', '#000'],
+            right: ['thick', '#000']
+          }
+        }
+      }
+    }
+  },
+  created() {},
+  mounted() {
+    this.init()
+  },
+  methods: {
+    init() {
+      if (this.sheet) return
+      this.sheet = new Spreadsheet('#excel', this.excelOptions).loadData(this.data).change((d) => {
+        console.log(d)
+      }) // load data
+      this.sheet.on('cell-selected', (cell, ri, ci) => {
+        // data.rows[ri].cells[ci]可以确定到指定单元格
+        console.log(cell)
+        console.log(ri)
+        console.log(ci)
+      })
+      this.sheet.on('cell-edited', (text, ri, ci) => {
+        console.log(text)
+        console.log(ri)
+        console.log(ci)
+      })
+    },
+    reRender(excelData) {
+      this.sheet.loadData(excelData)
+      this.sheet.reRender()
+    }
+  }
+}
+</script>
+

+ 378 - 0
java代码/ruoyi-ui/src/components/free/filter-page-table.vue

@@ -0,0 +1,378 @@
+<template>
+  <div id="data-table">
+    <el-form v-if="useFilter" :model="searchInfo" :inline="true" style="padding:0.9rem·1.875rem" size="mini">
+      <el-row type="flex">
+        <el-col :span="22">
+          <el-form-item v-for="(item, index) in filterList" :key="index">
+            <template v-if="item.filter === 'select'">
+              <el-select
+                v-model="searchInfo[item.prop]"
+                size="mini"
+                clearable
+                filterable
+                :placeholder="`请选择${item.label}`"
+                @clear="toClear(item.prop)"
+                @change="data => filterReturn(data, item)"
+              >
+                <slot name="options" v-bind="{ item }" />
+              </el-select>
+            </template>
+            <template v-else-if="item.filter === 'date'">
+              <el-date-picker
+                v-model="searchInfo[item.prop]"
+                value-format="yyyy-MM-dd"
+                format="yyyy-MM-dd"
+                type="daterange"
+                range-separator="-"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                clearable
+              />
+            </template>
+            <template v-else>
+              <el-input v-model="searchInfo[item.prop]" clearable size="mini" :placeholder="`请输入${item.label}`" @clear="toClear(item.prop)" />
+            </template>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" size="mini" @click="filterSearch">查询</el-button>
+          </el-form-item>
+        </el-col>
+        <el-col :span="2" style="text-align:right">
+          <slot name="selfbtn" />
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <el-table
+      ref="table"
+      row-key="id"
+      :data="data"
+      border
+      stripe
+      size="mini"
+      :max-height="height !== null ? height : ''"
+      v-bind="options"
+      :show-summary="useSum"
+      :summary-method="computedSum"
+      @select="handleSelectionChange"
+      @select-all="handleSelectAll"
+      @row-click="rowClick"
+    >
+      <el-table-column v-if="select" type="selection" width="55" prop="id" :reserve-selection="true" />
+      <template v-for="(item, index) in fields">
+        <template v-if="!item.notable">
+          <template v-if="item.custom">
+            <el-table-column :key="index" align="center" :label="item.label" v-bind="item.options" :show-overflow-tooltip="item.showTip || true">
+              <template #default="{ row }">
+                <slot name="custom" v-bind="{ item, row }" />
+              </template>
+            </el-table-column>
+          </template>
+          <template v-else>
+            <el-table-column
+              :key="index"
+              align="center"
+              :label="item.label"
+              :prop="item.prop"
+              :formatter="toFormatter"
+              sortable
+              v-bind="item.options"
+              :show-overflow-tooltip="item.showTip || true"
+            />
+          </template>
+        </template>
+      </template>
+      <template v-if="opera.length > 0">
+        <el-table-column label="操作" align="center" :width="operaWidth">
+          <template #default="{ row, $index }">
+            <template v-for="(item, index) in opera">
+              <template v-if="display(item, row)">
+                <el-tooltip v-if="item.icon" :key="index" effect="dark" :content="item.label" placement="bottom">
+                  <el-button
+                    :key="index"
+                    type="text"
+                    :icon="item.icon || ''"
+                    size="mini"
+                    @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index)"
+                  />
+                </el-tooltip>
+                <!-- <el-button v-else :key="index" type="text" size="mini" @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index)">
+                  {{ item.label }}
+                </el-button> -->
+                <el-link
+                  v-else
+                  :key="`${item.model}-column-${index}`"
+                  :type="item.type || 'primary'"
+                  :icon="item.icon || ''"
+                  size="mini"
+                  style="padding-right:10px"
+                  :underline="false"
+                  @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index)"
+                >
+                  {{ item.label }}
+                </el-link>
+              </template>
+            </template>
+          </template>
+        </el-table-column>
+      </template>
+    </el-table>
+    <el-row v-if="usePage" type="flex" align="middle" justify="end" style="padding-top:1rem">
+      <el-col :span="24" style="text-align:right;">
+        <el-pagination
+          background
+          layout="total, prev, pager, next"
+          :page-sizes="[10, 15, 20, 50, 100]"
+          :total="total"
+          :page-size="limit"
+          :current-page.sync="currentPage"
+          @current-change="changePage"
+          @size-change="sizeChange"
+        />
+        <!-- sizes -->
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash'
+export default {
+  name: 'DataTable',
+  props: {
+    fields: { type: Array, required: true },
+    data: { type: Array, required: true },
+    opera: { type: Array, default: () => [] },
+    toFormat: null,
+    height: null,
+    select: { type: Boolean, default: false },
+    selected: { type: Array, default: () => [] },
+    usePage: { type: Boolean, default: true },
+    total: { type: Number, default: 0 },
+    options: null,
+    useSum: { type: Boolean, default: false },
+    sumcol: { type: Array, default: () => [] },
+    sumres: { type: String, default: 'total' },
+    filter: { type: Array, default: () => [] },
+    operaWidth: { type: Number, default: 200 },
+    limit: { type: Number, default: _.get(this, `$limit`, undefined) !== undefined ? this.$limit : process.env.VUE_APP_LIMIT * 1 || 10 }
+  },
+  components: {},
+  data: () => ({
+    pageSelected: [],
+    currentPage: 1,
+    // limit: _.get(this, `$limit`, undefined) !== undefined ? this.$limit : process.env.VUE_APP_LIMIT * 1 || 10,
+    searchInfo: {},
+    useFilter: true,
+    filterList: []
+  }),
+  computed: {},
+  watch: {
+    selected: {
+      handler(val) {
+        if (val.length > 0) {
+          this.pageSelected = val
+          this.initSelection()
+        }
+      },
+      immediate: true
+    },
+    data: {
+      handler(val, oval) {
+        if (this.select) {
+          this.initSelection()
+        }
+      }
+    },
+    fields: {
+      handler(val, oval) {
+        if (val) this.getFilterList()
+      },
+      immediate: true
+    }
+  },
+  created() {},
+  methods: {
+    toFormatter(row, column, cellValue, index) {
+      const this_fields = this.fields.filter(fil => fil.prop === column.property)
+      if (this_fields.length > 0) {
+        const format = _.get(this_fields[0], `format`, false)
+        if (format) {
+          let res
+          if (_.isFunction(format)) {
+            res = format(cellValue)
+          } else {
+            res = this.toFormat({
+              model: this_fields[0].prop,
+              value: cellValue
+            })
+          }
+          return res
+        } else return cellValue
+      }
+    },
+    handleOpera(data, method, confirm = false, methodZh, label, index) {
+      let self = true
+      if (_.isFunction(methodZh)) {
+        methodZh = methodZh(data)
+      } else if (!_.isString(methodZh)) {
+        methodZh = label
+        self = false
+      }
+      if (confirm) {
+        this.$confirm(self ? methodZh : `您确认${methodZh}该数据?`, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+          .then(() => {
+            this.$emit(method, { data, index })
+          })
+          .catch(() => {})
+      } else {
+        this.$emit(method, { data, index })
+      }
+    },
+    handleSelectionChange(selection, row) {
+      // console.log(selection);
+      // console.log(row);
+      // 根据row是否再pageSelected中,判断是添加还是删除
+      let res = []
+      if (this.pageSelected.find(i => i.id === row.id)) {
+        res = this.pageSelected.filter(f => f.id !== row.id)
+      } else {
+        this.pageSelected.push(row)
+        res = this.pageSelected
+      }
+      this.$set(this, `pageSelected`, res)
+      this.$emit(`handleSelect`, _.uniqBy(res, 'id'))
+    },
+    handleSelectAll(selection) {
+      // 处于没全选状态,选择之后一定是全选,只有处于全选状态时,才会反选(全取消)
+      // console.log(selection);
+      let res = []
+      if (selection.length > 0) {
+        // 全选
+        res = _.uniqBy(this.pageSelected.concat(selection), 'id')
+      } else {
+        // 全取消
+        res = _.differenceBy(this.pageSelected, this.data, 'id')
+      }
+      this.$set(this, `pageSelected`, res)
+      this.$emit(`handleSelect`, res)
+    },
+    initSelection() {
+      this.$nextTick(() => {
+        this.$refs.table.clearSelection()
+        this.selected.forEach(info => {
+          const d = this.data.filter(p => p.id === info.id)
+          if (d.length > 0) this.$refs.table.toggleRowSelection(d[0])
+        })
+      })
+    },
+    selectReset() {
+      this.$refs.table.clearSelection()
+    },
+    display(item, row) {
+      const display = _.get(item, `display`, true)
+      if (display === true) return true
+      else {
+        const res = display(row)
+        return res
+      }
+    },
+    //
+    changePage(page = this.currentPage) {
+      this.$emit('query', { skip: (page - 1) * this.limit, limit: this.limit, ...this.searchInfo })
+    },
+    sizeChange(limit) {
+      this.limit = limit
+      this.currentPage = 1
+      this.$emit('query', { skip: 0, limit: this.limit, ...this.searchInfo })
+    },
+    getFilterList() {
+      let res = this.fields.filter(f => _.get(f, 'filter', false))
+      this.$set(this, `useFilter`, res.length > 0)
+      res.map(i => {
+        if (i.filter === 'date' && this.searchInfo[i.porp] === undefined) this.$set(this.searchInfo, i.prop, [])
+      })
+      res = [...res, ...this.filter]
+      this.$set(this, `filterList`, res)
+    },
+    filterSearch() {
+      this.currentPage = 1
+      this.$emit('query', { skip: 0, limit: this.limit, ...this.searchInfo })
+    },
+    rowClick(row, column, event) {
+      this.$emit(`rowClick`, row)
+    },
+    toClear(prop) {
+      delete this.searchInfo[prop]
+    },
+    filterReturn(data, item) {
+      const { prop, filterReturn } = item
+      if (filterReturn) this.$emit('filterReturn', { data, prop })
+    },
+    // 计算合计
+    computedSum({ columns, data }) {
+      if (columns.length <= 0 || data.length <= 0) return ''
+      const result = []
+      const reg = new RegExp(/^\d+$/)
+      for (const column of columns) {
+        // 判断有没有prop属性
+        const prop = _.get(column, 'property')
+        if (!prop) {
+          result.push('')
+          continue
+        }
+        // 判断是否需要计算
+        const inlist = this.sumcol.find(f => f == prop)
+        if (!inlist) {
+          result.push('')
+          continue
+        }
+        let res = 0
+        // 整理出要计算的属性(只取出数字或者可以为数字的值)
+        const resetList = data.map(i => {
+          const d = _.get(i, prop)
+          const res = reg.test(d)
+          if (res) return d * 1
+          else return 0
+        })
+        if (this.sumres === 'total') {
+          res = this.totalComputed(resetList)
+        } else if (this.sumres === 'avg') {
+          res = this.avgComputed(resetList)
+        } else if (this.sumres === 'max') {
+          res = this.maxComputed(resetList)
+        } else if (this.sumres === 'min') {
+          res = this.minComputed(resetList)
+        }
+        result.push(res)
+      }
+      result[0] = '合计'
+      return result
+    },
+    // 合计计算
+    totalComputed(data) {
+      const total = data.reduce((p, n) => p + n, 0)
+      return total
+    },
+    // 平均值计算
+    avgComputed(data) {
+      const total = this.totalComputed(data)
+      return _.round(_.divide(total, data.length), 2)
+    },
+    // 最大值计算
+    maxComputed(data) {
+      return _.max(data)
+    },
+    // 最小值计算
+    minComputed(data) {
+      return _.min(data)
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped></style>

+ 140 - 9
java代码/ruoyi-ui/src/views/merits/template/index.vue

@@ -1,29 +1,160 @@
 <template>
 <template>
   <div id="index">
   <div id="index">
-    <template v-if="view==='list'">
-      <list-view />
+    <template>
+      <list-view v-show="view==='list'" style="padding:20px" @toAdd="toAdd" />
     </template>
     </template>
-    <template v-else>
-      detail
+    <template v-if="view==='detail'">
+      <el-row style="padding:20px" type="flex">
+        <el-col :span="4">
+          <el-button @click="returnList">返回</el-button>
+        </el-col>
+        <el-col :span="4">
+          <el-button type="primary" @click="toSave">保存</el-button>
+        </el-col>
+        <el-col :span="4">
+          <el-upload action="/" :http-request="toImport" :show-file-list="false" accept=".xlsx,.xls">
+            <template #trigger>
+              <el-button type="warning">导入模板</el-button>
+            </template>
+          </el-upload>
+        </el-col>
+      </el-row>
+      <excel-view ref="excelView" v-model="data" :view="viewStyle" />
     </template>
     </template>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
+import excelView from '@/components/free/excel-view.vue'
 import listView from './parts/list.vue'
 import listView from './parts/list.vue'
-import { mapState, createNamespacedHelpers } from 'vuex'
+import XLSX from 'xlsx'
+const _ = require('lodash')
+import { createNamespacedHelpers } from 'vuex'
+const { mapActions } = createNamespacedHelpers('template')
 export default {
 export default {
   name: 'Index',
   name: 'Index',
-  components: { listView },
+  components: { listView, excelView },
   props: {},
   props: {},
   data: function() {
   data: function() {
     return {
     return {
-      view: 'list'
+      view: 'list',
+      data: {},
+      viewStyle: {
+        height: () => document.documentElement.clientHeight - 200,
+        width: () => 800
+      }
     }
     }
   },
   },
   created() {},
   created() {},
-  methods: {}
+  methods: {
+    ...mapActions({}),
+    toAdd() {
+      this.view = 'detail'
+    },
+    async toEdit() {
+      // 请求数据,修改
+      console.log('line 45 in function:toEdit')
+    },
+    async toSave() {
+      const dup = _.cloneDeep(this.data)
+      console.log(dup)
+    },
+    returnList() {
+      this.view = 'list'
+    },
+    toImport({ file }) {
+      this.getWorkbook(file)
+    },
+    getWorkbook(fileSelected) {
+      const file = fileSelected
+      const reader = new FileReader()
+      reader.onload = (e) => {
+        const data = e.target.result
+        const fixedData = this.fixData(data)
+        const workbook = XLSX.read(btoa(fixedData), { type: 'base64' })
+        const excelData = this.stox(workbook)
+        this.$set(this, 'data', excelData)
+        this.$refs.excelView.reRender(excelData)
+      }
+      reader.readAsArrayBuffer(file)
+    },
+
+    fixData(data) {
+      let o = ''
+      let l = 0
+      const w = 10240
+      for (; l < data.byteLength / w; ++l) o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)))
+      o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
+      return o
+    },
+    stox(wb) {
+      const out = {}
+      const mergeCells = []
+      let resMerges = [] // 没有内容,也需要合并
+      wb.SheetNames.forEach((name) => {
+        const o = { name: name, rows: {}}
+        const ws = wb.Sheets[name]
+        const merges = ws['!merges']
+        const aoa = XLSX.utils.sheet_to_json(ws, { raw: false, header: 1 })
+        aoa.forEach((r, i) => {
+          const cells = {}
+          r.forEach((c, j) => {
+            let merge = [0, 0]
+            const spo = { r: i, c: j }
+            const res = merges.find((f) => _.isEqual(f.s, spo))
+            if (res) {
+              const resIndex = merges.findIndex((f) => _.isEqual(f.s, spo))
+              merges.splice(resIndex, 1)
+              const { e, s } = res
+              // res计算合并的行和列
+              const rowSpan = e.r - s.r
+              const colSpan = e.c - s.c
+              // 计算合并单元格的起始-终止位置, row + 1为数字部分;col去换
+              mergeCells.push(`${this.getCellLetter(s.c)}${s.r + 1}:${this.getCellLetter(e.c)}${e.r + 1}`)
+              merge = [rowSpan, colSpan]
+            }
+            cells[j] = { text: c, merge, from: 'excel' } // 加上merge
+          })
+          out[i] = { cells: cells }
+        })
+        resMerges = merges
+      })
+      for (const cell of resMerges) {
+        const { e, s } = cell
+        // 使其内容为一个单元格
+        mergeCells.push(`${this.getCellLetter(s.c)}${s.r + 1}:${this.getCellLetter(e.c)}${e.r + 1}`)
+        // 使其样式为一个单元格
+        const rowSpan = e.r - s.r
+        const colSpan = e.c - s.c
+        const merge = [rowSpan, colSpan]
+        const text = ''
+        // 确定行,列
+        const row = s.r
+        const col = s.c
+        out[row].cells[col] = { merge, text }
+      }
+      return { merges: mergeCells, rows: out }
+    },
+    getCellLetter(number) {
+      // 来的是索引
+      number = 65 + number
+      return String.fromCharCode(number)
+    }
+  }
 }
 }
 </script>
 </script>
 
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+/deep/.el-upload--text {
+  background-color: #fff;
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  box-sizing: border-box;
+  width: auto;
+  height: auto;
+  text-align: center;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+</style>

+ 37 - 11
java代码/ruoyi-ui/src/views/merits/template/parts/list.vue

@@ -1,35 +1,61 @@
 <template>
 <template>
   <div id="list">
   <div id="list">
-    <el-row style="padding:20px">
+    <el-row>
       <el-col :span="24">
       <el-col :span="24">
-        <el-table :data="list" border>
-          <el-table-column align="center" label="模板名称" prop="title" />
-          <el-table-column align="center" label="是否启用" prop="is_use" />
-          <el-table-column align="center" label="操作" prop="opera" />
-        </el-table>
+        <data-table :fields="fields" :opera="opera" :total="total" :data="list" @query="search">
+          <template #selfbtn>
+            <el-button type="primary" @click="toCreate">添加</el-button>
+          </template>
+        </data-table>
       </el-col>
       </el-col>
     </el-row>
     </el-row>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
+import dataTable from '@/components/free/filter-page-table.vue'
 import { mapState, createNamespacedHelpers } from 'vuex'
 import { mapState, createNamespacedHelpers } from 'vuex'
 const { mapActions } = createNamespacedHelpers('template')
 const { mapActions } = createNamespacedHelpers('template')
 export default {
 export default {
   name: 'List',
   name: 'List',
-  components: {},
+  components: { dataTable },
   props: {},
   props: {},
   data: function() {
   data: function() {
     return {
     return {
-      list: []
+      list: [],
+      total: 0,
+      fields: [
+        { label: '模板标题', prop: 'title', filter: true },
+        { label: '使用状态', prop: 'is_use', format: i => i ? '启用' : '禁用' }
+      ],
+      opera: [
+        { label: '编辑', method: 'toEdit' },
+        { label: '删除', method: 'toDelete', type: 'danger' }
+      ]
     }
     }
   },
   },
-  created() {},
+  created() {
+    this.search()
+  },
   methods: {
   methods: {
     ...mapActions(['query', 'delete']),
     ...mapActions(['query', 'delete']),
-    async search() {}
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      const res = await this.query({ skip, limit, ...info })
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data)
+        this.$set(this, `total`, res.total)
+      }
+    },
+    async toDelete({ data }) {},
+    async toEdit({ data }) {},
+    async toCreate() {
+      this.$emit('toAdd')
+    }
   }
   }
 }
 }
 </script>
 </script>
 
 
-<style lang="less" scoped></style>
+<style lang="scss" scoped>
+.main {
+  padding: 25px 30px 30px;
+}</style>