oss.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. <template>
  2. <hc-card>
  3. <template #header>
  4. <div class="w-40">
  5. <el-select v-model="searchForm.category" placeholder="请选择分类" filterable clearable block>
  6. <el-option v-for="item in categoryData" :key="item.value" :label="item.label" :value="item.value" />
  7. </el-select>
  8. </div>
  9. <div class="ml-2 w-32">
  10. <el-select v-model="searchType" placeholder="选择搜索类型" filterable block>
  11. <el-option label="资源编号" value="ossCode" />
  12. <el-option label="accessKey" value="accessKey" />
  13. </el-select>
  14. </div>
  15. <div class="ml-2 w-60">
  16. <hc-search-input v-model="searchName" placeholder="请输入关键词" @search="searchClick" />
  17. </div>
  18. </template>
  19. <template #extra>
  20. <el-button hc-btn type="primary" @click="addClick">新增</el-button>
  21. <el-button hc-btn type="danger" @click="delClick">删除</el-button>
  22. </template>
  23. <hc-table
  24. :column="tableColumn" :datas="tableData" :loading="tableLoading"
  25. :is-index="false" is-check :check-style="{ width: 29 }"
  26. @selection-change="tableCheckChange"
  27. >
  28. <template #category="{ row }">{{ getCategoryName(row) }}</template>
  29. <template #status="{ row }">{{ row.statusName ?? '否' }}</template>
  30. <template #action="{ row }">
  31. <el-link type="warning" @click="editRowClick(row)">修改</el-link>
  32. <el-link v-del-com:[delRowClick]="row" type="danger">删除</el-link>
  33. <el-link type="success" @click="debugRowClick(row)">调试</el-link>
  34. <el-link v-yes-com:[enableRowClick]="row" type="primary">启用</el-link>
  35. </template>
  36. </hc-table>
  37. <template #action>
  38. <hc-pages :pages="searchForm" @change="pageChange" />
  39. </template>
  40. <!-- 新增/修改 -->
  41. <hc-dialog v-model="isDialogShow" widths="60rem" is-footer-center :title="dialogTitle" @close="dialogClose">
  42. <el-form ref="formRef" :model="formModel" :rules="formRules" label-position="top" label-width="auto">
  43. <el-row :gutter="20">
  44. <el-col :span="12">
  45. <el-form-item label="存储分类:" prop="category">
  46. <div class="form-item-div">
  47. <el-radio-group v-model="formModel.category">
  48. <el-radio v-for="item in categoryData" :key="item.value" :value="item.value">{{ item.label }}</el-radio>
  49. </el-radio-group>
  50. </div>
  51. </el-form-item>
  52. </el-col>
  53. <el-col :span="12">
  54. <el-form-item label="资源编号:" prop="ossCode">
  55. <el-input v-model="formModel.ossCode" clearable />
  56. </el-form-item>
  57. </el-col>
  58. <el-col :span="12">
  59. <el-form-item label="资源地址:" prop="endpoint">
  60. <el-input v-model="formModel.endpoint" clearable />
  61. </el-form-item>
  62. </el-col>
  63. <el-col :span="12">
  64. <el-form-item label="空间名:" prop="bucketName">
  65. <el-input v-model="formModel.bucketName" clearable />
  66. </el-form-item>
  67. </el-col>
  68. <el-col :span="12">
  69. <el-form-item label="accessKey:" prop="accessKey">
  70. <el-input v-model="formModel.accessKey" clearable />
  71. </el-form-item>
  72. </el-col>
  73. <el-col :span="12">
  74. <el-form-item label="secretKey:" prop="secretKey">
  75. <el-input v-model="formModel.secretKey" clearable />
  76. </el-form-item>
  77. </el-col>
  78. <el-col v-if="formModel.category === 4" :span="12">
  79. <el-form-item label="appId:">
  80. <el-input v-model="formModel.appId" clearable />
  81. </el-form-item>
  82. </el-col>
  83. <el-col v-if="formModel.category === 4" :span="12">
  84. <el-form-item label="region:">
  85. <el-input v-model="formModel.region" clearable />
  86. </el-form-item>
  87. </el-col>
  88. <el-col :span="12">
  89. <el-form-item label="备注说明:">
  90. <el-input v-model="formModel.remark" clearable />
  91. </el-form-item>
  92. </el-col>
  93. </el-row>
  94. </el-form>
  95. <template #footer>
  96. <el-button hc-btn @click="dialogClose">取消</el-button>
  97. <el-button hc-btn type="primary" :loading="submitLoading" @click="dialogSubmit">提交</el-button>
  98. </template>
  99. </hc-dialog>
  100. <!-- 上传调试 -->
  101. <hc-dialog v-model="isDebugShow" widths="400px" :footer="false" title="上传调试" @close="debugClose">
  102. <el-form :model="debugForm" label-position="top" label-width="auto">
  103. <el-form-item label="资源编号:">
  104. <el-input v-model="debugForm.ossCode" disabled />
  105. </el-form-item>
  106. <el-form-item label="上传图片:">
  107. <el-upload class="avatar-uploader" :headers="getHeader()" :action="actionUrl" accept="image/*" :show-file-list="false" :on-success="handleAvatarSuccess">
  108. <img v-if="debugForm.url" :src="debugForm.url" class="avatar" alt="">
  109. <hc-icon v-else class="avatar-uploader-icon" name="add-large" />
  110. </el-upload>
  111. </el-form-item>
  112. <el-form-item label="回调地址:">
  113. <el-input v-model="debugForm.url" />
  114. </el-form-item>
  115. </el-form>
  116. </hc-dialog>
  117. </hc-card>
  118. </template>
  119. <script setup>
  120. import { nextTick, onActivated, ref } from 'vue'
  121. import { HcDelMsg, getHeader } from 'hc-vue3-ui'
  122. import { getDictionaryData } from '~uti/tools'
  123. import { arrToId, formValidate, getArrValue, isNullES } from 'js-fast-way'
  124. import mainApi from '~api/resource/oss'
  125. //激活
  126. onActivated(() => {
  127. getDataApi()
  128. })
  129. const getDataApi = async () => {
  130. await getCategoryData()
  131. searchClick()
  132. }
  133. //搜索表单
  134. const searchType = ref('ossCode')
  135. const searchName = ref('')
  136. const searchForm = ref({ category: null, current: 1, size: 30, total: 0 })
  137. //获取字典数据
  138. const categoryData = ref([])
  139. const getCategoryData = async () => {
  140. categoryData.value = await getDictionaryData('oss')
  141. }
  142. const getCategoryName = ({ category }) => {
  143. if (isNullES(category)) return '-'
  144. const item = categoryData.value.find((item) => item.value === category)
  145. if (isNullES(item)) return category
  146. return item.label ?? category
  147. }
  148. //搜索
  149. const searchClick = () => {
  150. searchForm.value.current = 1
  151. getTableData()
  152. }
  153. //分页
  154. const pageChange = ({ current, size }) => {
  155. searchForm.value.current = current
  156. searchForm.value.size = size
  157. getTableData()
  158. }
  159. //表格数据
  160. const tableColumn = ref([
  161. { key: 'category', name: '分类', width: 80, align: 'center' },
  162. { key: 'ossCode', name: '资源编号', width: 120 },
  163. { key: 'endpoint', name: '资源地址' },
  164. { key: 'bucketName', name: '空间名' },
  165. { key: 'accessKey', name: 'accessKey' },
  166. { key: 'secretKey', name: 'secretKey' },
  167. { key: 'remark', name: '备注' },
  168. { key: 'status', name: '是否启用', width: 80, align: 'center' },
  169. { key: 'action', name: '操作', width: 160, align: 'center' },
  170. ])
  171. //获取表格数据
  172. const tableLoading = ref(true)
  173. const tableData = ref([])
  174. const getTableData = async () => {
  175. tableData.value = []
  176. tableLoading.value = true
  177. const { error, code, data } = await mainApi.page({
  178. ...searchForm.value,
  179. total: null,
  180. })
  181. tableLoading.value = false
  182. if (!error && code === 200) {
  183. tableData.value = getArrValue(data['records'])
  184. searchForm.value.total = data['total']
  185. } else {
  186. tableData.value = []
  187. searchForm.value.total = 0
  188. }
  189. }
  190. //表格被选择
  191. const tableCheckKeys = ref([])
  192. const tableCheckChange = (rows) => {
  193. tableCheckKeys.value = rows
  194. }
  195. //新增/修改 弹窗
  196. const isDialogShow = ref(false)
  197. const dialogTitle = ref('')
  198. //表单
  199. const formRef = ref(null)
  200. const formModel = ref({})
  201. const formRules = {
  202. category: {
  203. required: true,
  204. trigger: 'blur',
  205. message: '请选择存储分类',
  206. },
  207. ossCode: {
  208. required: true,
  209. trigger: 'blur',
  210. message: '请输入资源编号',
  211. },
  212. endpoint: {
  213. required: true,
  214. trigger: 'blur',
  215. message: '请输入资源地址',
  216. },
  217. bucketName: {
  218. required: true,
  219. trigger: 'blur',
  220. message: '请输入空间名',
  221. },
  222. accessKey: {
  223. required: true,
  224. trigger: 'blur',
  225. message: '请输入accessKey',
  226. },
  227. secretKey: {
  228. required: true,
  229. trigger: 'blur',
  230. message: '请输入secretKey',
  231. },
  232. }
  233. //新增
  234. const addClick = () => {
  235. dialogTitle.value = '新增对象存储'
  236. formModel.value = { category: 1 }
  237. //显示表单弹窗
  238. nextTick(() => {
  239. isDialogShow.value = true
  240. })
  241. }
  242. //修改
  243. const editRowClick = (row) => {
  244. formModel.value = {}
  245. dialogTitle.value = '修改对象存储'
  246. formModel.value = { ...row }
  247. //显示表单弹窗
  248. nextTick(() => {
  249. isDialogShow.value = true
  250. })
  251. }
  252. //提交表单
  253. const submitLoading = ref(false)
  254. const dialogSubmit = async () => {
  255. const formRes = await formValidate(formRef.value)
  256. if (!formRes) return false
  257. submitLoading.value = true
  258. //发起请求
  259. const { error, code, msg } = await mainApi.submit(formModel.value)
  260. submitLoading.value = false
  261. if (!error && code === 200) {
  262. dialogClose()
  263. window?.$message?.success('操作成功')
  264. getTableData().then()
  265. } else {
  266. window?.$message?.error(msg ?? '操作失败')
  267. }
  268. }
  269. //关闭弹窗
  270. const dialogClose = () => {
  271. isDialogShow.value = false
  272. submitLoading.value = false
  273. formModel.value = {}
  274. }
  275. //上传调试
  276. const isDebugShow = ref(false)
  277. const debugForm = ref({})
  278. const actionUrl = ref('')
  279. const debugRowClick = (row) => {
  280. debugForm.value = { ossCode: row.ossCode, url: '' }
  281. actionUrl.value = '/api/blade-resource/oss/endpoint/put-file?code=' + row.ossCode
  282. isDebugShow.value = true
  283. }
  284. const handleAvatarSuccess = ({ code, data, msg }) => {
  285. if (code !== 200 || isNullES(data.link)) {
  286. window.$message.error(msg ?? '上传失败')
  287. return false
  288. }
  289. window.$message.success('上传成功')
  290. debugForm.value.url = data.link
  291. }
  292. const debugClose = () => {
  293. isDebugShow.value = false
  294. debugForm.value = {}
  295. }
  296. //启用
  297. const enableRowClick = async ({ item }, resolve) => {
  298. const { code, msg } = await mainApi.enable(item.id)
  299. if (code === 200) {
  300. resolve()
  301. window.$message.success('启用成功')
  302. getTableData().then()
  303. } else {
  304. resolve()
  305. window.$message.error(msg ?? '启用失败')
  306. }
  307. }
  308. //删除
  309. const delRowClick = async ({ item }, resolve) => {
  310. const { code, msg } = await mainApi.del(item.id)
  311. if (code === 200) {
  312. resolve()
  313. window.$message.success('删除成功')
  314. getTableData().then()
  315. } else {
  316. resolve()
  317. window.$message.error(msg ?? '删除失败')
  318. }
  319. }
  320. //批量删除
  321. const delClick = () => {
  322. const rows = tableCheckKeys.value
  323. if (rows.length <= 0) {
  324. window.$message.warning('请选择要删除的数据')
  325. return false
  326. }
  327. //确认删除菜单
  328. HcDelMsg(async (resolve) => {
  329. //发起请求
  330. const ids = arrToId(rows)
  331. const { code, msg } = await mainApi.del(ids)
  332. //关闭弹窗的回调
  333. resolve()
  334. //处理结果
  335. if (code === 200) {
  336. window.$message.success('删除成功')
  337. getTableData().then()
  338. } else {
  339. window.$message.error(msg ?? '删除失败')
  340. }
  341. })
  342. }
  343. </script>
  344. <style lang="scss">
  345. .avatar-uploader {
  346. width: 100%;
  347. height: 178px;
  348. .el-upload {
  349. width: 100%;
  350. height: 178px;
  351. border: 1px dashed #dcdfe6;
  352. border-radius: 6px;
  353. cursor: pointer;
  354. position: relative;
  355. overflow: hidden;
  356. transition: var(--el-transition-duration-fast);
  357. }
  358. .el-upload:hover {
  359. border-color: var(--el-color-primary);
  360. }
  361. .avatar {
  362. width: 100%;
  363. height: 100%;
  364. border-radius: 6px;
  365. display: block;
  366. }
  367. .avatar-uploader-icon {
  368. font-size: 28px;
  369. color: #8c939d;
  370. }
  371. }
  372. </style>