优化素材中心

This commit is contained in:
xiangheng
2024-02-21 19:27:18 +08:00
parent 1dc7f7542a
commit aa2ecf115b
13 changed files with 215 additions and 249 deletions

View File

@@ -1,10 +1,12 @@
<!DOCTYPE html> <!doctype html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>后台管理系统</title> <title>x_admin管理系统</title>
<meta name="description" content="后台管理系统" />
<meta name="keywords" content="x_admin,后台管理系统" />
<style> <style>
* { * {
margin: 0; margin: 0;
@@ -40,7 +42,6 @@
} }
@keyframes loading-dash { @keyframes loading-dash {
0% { 0% {
stroke-dasharray: 90, 150; stroke-dasharray: 90, 150;
stroke-dashoffset: -40px; stroke-dashoffset: -40px;
@@ -61,6 +62,63 @@
</svg> </svg>
</div> </div>
</div> </div>
<!-- 异常收集 -->
<script>
!(function (c, i, e, b) {
var h = i.createElement('script')
var f = i.getElementsByTagName('script')[0]
h.type = 'text/javascript'
h.crossorigin = true
h.onload = function () {
new c[b]['Monitor']().init({
id: '3HYtHZVKJQxGvKyR',
sendSuspicious: true,
sendSpaPv: true
})
}
f.parentNode.insertBefore(h, f)
h.src = e
})(window, document, 'https://sdk.51.la/perf/js-sdk-perf.min.js', 'LingQue')
</script>
<!-- 网站统计 -->
<script>
!(function (p) {
'use strict'
!(function (t) {
var s = window,
e = document,
i = p,
c = ''.concat(
'https:' === e.location.protocol ? 'https://' : 'http://',
'sdk.51.la/js-sdk-pro.min.js'
),
n = e.createElement('script'),
r = e.getElementsByTagName('script')[0]
;(n.type = 'text/javascript'),
n.setAttribute('charset', 'UTF-8'),
(n.async = !0),
(n.src = c),
(n.id = 'LA_COLLECT'),
(i.d = n)
var o = function () {
s.LA.ids.push(i)
}
s.LA ? s.LA.ids && o() : ((s.LA = p), (s.LA.ids = []), o()),
r.parentNode.insertBefore(n, r)
})()
})({ id: '3HYtjw7cm3a6D52W', ck: '3HYtjw7cm3a6D52W', autoTrack: true, hashMode: true })
</script>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
<!-- 百度统计 -->
<!-- <script>
var _hmt = _hmt || []
;(function () {
var hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?9ce7f08804f25b8da927f382b23cf456'
var s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
})()
</script> -->
</body> </body>
</html> </html>

View File

@@ -1,7 +1,13 @@
<template> <template>
<div> <div>
<div class="file-item relative" :style="{ height: fileSize, width: fileSize }"> <div class="file-item relative" :style="{ height: fileSize, width: fileSize }">
<el-image class="image" v-if="type == 'image'" fit="contain" :src="uri"></el-image> <el-image
class="image"
v-if="type == 'image'"
fit="contain"
loading="lazy"
:src="uri"
></el-image>
<video class="video" v-else-if="type == 'video'" :src="uri"></video> <video class="video" v-else-if="type == 'video'" :src="uri"></video>
<div <div
v-if="type == 'video'" v-if="type == 'video'"

View File

@@ -20,7 +20,7 @@ export function useCate(type: number) {
const cateLists = ref<any[]>([]) const cateLists = ref<any[]>([])
// 选中的分组id // 选中的分组id
const cateId = ref<number | string>('') const cateId = ref<number | string>(0)
// 获取分组列表 // 获取分组列表
const getCateLists = async () => { const getCateLists = async () => {
@@ -28,10 +28,10 @@ export function useCate(type: number) {
type type
}) })
const item: any[] = [ const item: any[] = [
// { {
// name: '全部', name: '全部',
// id: '' id: 0
// }, }
// { // {
// name: '未分组', // name: '未分组',
// id: 0 // id: 0
@@ -96,7 +96,6 @@ export function useFile(
size: number size: number
) { ) {
const tableRef = shallowRef() const tableRef = shallowRef()
const listShowType = ref('normal')
const moveId = ref(0) const moveId = ref(0)
const select = ref<any[]>([]) const select = ref<any[]>([])
const isCheckAll = ref(false) const isCheckAll = ref(false)
@@ -120,10 +119,6 @@ export function useFile(
resetPage() resetPage()
} }
const isSelect = (id: number) => {
return !!select.value.find((item: any) => item.id == id)
}
const batchFileDelete = async (id?: number[]) => { const batchFileDelete = async (id?: number[]) => {
await feedback.confirm( await feedback.confirm(
'确认删除后,本地或云存储文件也将同步删除,如文件已被使用,请谨慎操作!' '确认删除后,本地或云存储文件也将同步删除,如文件已被使用,请谨慎操作!'
@@ -167,7 +162,9 @@ export function useFile(
const cancelSelete = (id: number) => { const cancelSelete = (id: number) => {
select.value = select.value.filter((item: any) => item.id != id) select.value = select.value.filter((item: any) => item.id != id)
} }
const selectItems = (items: any[]) => {
select.value = items
}
const selectAll = (value: CheckboxValueType) => { const selectAll = (value: CheckboxValueType) => {
isIndeterminate.value = false isIndeterminate.value = false
tableRef.value?.toggleAllSelection() tableRef.value?.toggleAllSelection()
@@ -186,7 +183,6 @@ export function useFile(
getFileList() getFileList()
} }
return { return {
listShowType,
tableRef, tableRef,
moveId, moveId,
pager, pager,
@@ -199,10 +195,10 @@ export function useFile(
batchFileDelete, batchFileDelete,
batchFileMove, batchFileMove,
selectFile, selectFile,
isSelect,
clearSelect, clearSelect,
cancelSelete, cancelSelete,
selectAll, selectAll,
selectItems,
handleFileRename handleFileRename
} }
} }

View File

@@ -106,40 +106,6 @@
> >
<el-button type="primary">本地上传</el-button> <el-button type="primary">本地上传</el-button>
</upload> </upload>
<el-button
v-perms="['admin:common:album:albumDel']"
v-if="mode == 'page'"
:disabled="!select.length"
@click.stop="batchFileDelete()"
>
删除
</el-button>
<popup
v-perms="['admin:common:album:albumMove']"
v-if="mode == 'page'"
class="ml-3"
@confirm="batchFileMove"
:disabled="!select.length"
title="移动文件"
>
<template #trigger>
<el-button :disabled="!select.length">移动</el-button>
</template>
<div>
<span class="mr-5">移动文件至</span>
<el-select v-model="moveId" placeholder="请选择">
<template v-for="item in cateLists" :key="item.id">
<el-option
v-if="item.id !== ''"
:label="item.name"
:value="item.id"
></el-option>
</template>
</el-select>
</div>
</popup>
</div> </div>
<el-input <el-input
class="w-60" class="w-60"
@@ -155,100 +121,19 @@
</el-button> </el-button>
</template> </template>
</el-input> </el-input>
<div class="flex items-center ml-2">
<el-tooltip content="列表视图" placement="top">
<div
class="list-icon"
:class="{
select: listShowType == 'table'
}"
@click="listShowType = 'table'"
>
<icon name="local-icon-list-2" :size="18" />
</div>
</el-tooltip>
<el-tooltip content="平铺视图" placement="top">
<div
class="list-icon"
:class="{
select: listShowType == 'normal'
}"
@click="listShowType = 'normal'"
>
<icon name="el-icon-Menu" :size="18" />
</div>
</el-tooltip>
</div>
</div>
<div class="mt-3" v-if="mode == 'page'">
<el-checkbox
:disabled="!pager.lists.length"
v-model="isCheckAll"
@change="selectAll"
:indeterminate="isIndeterminate"
>
当页全选
</el-checkbox>
</div> </div>
<div class="material-center__content flex flex-col flex-1 mb-1 min-h-0"> <div class="material-center__content flex flex-col flex-1 mb-1 min-h-0">
<el-scrollbar v-if="pager.lists.length" v-show="listShowType == 'normal'">
<ul class="file-list flex flex-wrap mt-4">
<li
class="file-item-wrap"
v-for="item in pager.lists"
:key="item.id"
:style="{ width: fileSize }"
>
<del-wrap @close="batchFileDelete([item.id])">
<file-item
:uri="item.uri"
:file-size="fileSize"
:type="type"
@click="selectFile(item)"
>
<div class="item-selected" v-if="isSelect(item.id)">
<icon :size="24" name="el-icon-Check" color="#fff" />
</div>
</file-item>
</del-wrap>
<overflow-tooltip class="mt-1" :content="item.name" />
<div class="operation-btns flex items-center">
<popover-input
v-perms="['admin:common:album:albumRename']"
@confirm="handleFileRename($event, item.id)"
size="default"
:value="item.name"
width="400px"
:limit="50"
show-limit
teleported
>
<el-button type="primary" link> 重命名 </el-button>
</popover-input>
<el-button type="primary" link @click="handlePreview(item.uri)">
查看
</el-button>
</div>
</li>
</ul>
</el-scrollbar>
<el-table <el-table
ref="tableRef" ref="tableRef"
class="mt-4" class="mt-4"
v-show="listShowType == 'table'"
:data="pager.lists" :data="pager.lists"
width="100%" width="100%"
height="100%" height="100%"
size="large" size="large"
@row-click="selectFile" @selection-change="handleSelectionChange"
> >
<el-table-column width="55"> <el-table-column type="selection" width="55" />
<template #default="{ row }">
<el-checkbox :modelValue="isSelect(row.id)" @change="selectFile(row)" />
</template>
</el-table-column>
<el-table-column label="图片" width="100"> <el-table-column label="图片" width="100">
<template #default="{ row }"> <template #default="{ row }">
<file-item :uri="row.uri" file-size="50px" :type="type"></file-item> <file-item :uri="row.uri" file-size="50px" :type="type"></file-item>
@@ -305,7 +190,7 @@
<div class="material-center__footer flex justify-between items-center mt-2"> <div class="material-center__footer flex justify-between items-center mt-2">
<div class="flex"> <div class="flex">
<template v-if="mode == 'page'"> <template v-if="mode == 'page'">
<span class="mr-3"> <!-- <span class="mr-3">
<el-checkbox <el-checkbox
:disabled="!pager.lists.length" :disabled="!pager.lists.length"
v-model="isCheckAll" v-model="isCheckAll"
@@ -314,7 +199,7 @@
> >
当页全选 当页全选
</el-checkbox> </el-checkbox>
</span> </span> -->
<el-button <el-button
v-perms="['admin:common:album:albumDel']" v-perms="['admin:common:album:albumDel']"
:disabled="!select.length" :disabled="!select.length"
@@ -333,8 +218,6 @@
<el-button :disabled="!select.length">移动</el-button> <el-button :disabled="!select.length">移动</el-button>
</template> </template>
<div>
<span class="mr-5">移动文件至</span>
<el-select v-model="moveId" placeholder="请选择"> <el-select v-model="moveId" placeholder="请选择">
<template v-for="item in cateLists" :key="item.id"> <template v-for="item in cateLists" :key="item.id">
<el-option <el-option
@@ -344,7 +227,6 @@
></el-option> ></el-option>
</template> </template>
</el-select> </el-select>
</div>
</popup> </popup>
</template> </template>
</div> </div>
@@ -389,7 +271,6 @@
import { useCate, useFile } from './hook' import { useCate, useFile } from './hook'
import FileItem from './file.vue' import FileItem from './file.vue'
import Preview from './preview.vue' import Preview from './preview.vue'
import type { Ref } from 'vue'
const props = defineProps({ const props = defineProps({
fileSize: { fileSize: {
type: String, type: String,
@@ -442,7 +323,6 @@ const {
const { const {
tableRef, tableRef,
listShowType,
moveId, moveId,
pager, pager,
fileParams, fileParams,
@@ -453,14 +333,18 @@ const {
refresh, refresh,
batchFileDelete, batchFileDelete,
batchFileMove, batchFileMove,
selectFile,
isSelect,
clearSelect, clearSelect,
cancelSelete, cancelSelete,
selectAll,
selectItems,
handleFileRename handleFileRename
} = useFile(cateId, typeValue, limit, props.pageSize) } = useFile(cateId, typeValue, limit, props.pageSize)
function handleSelectionChange(val: any[]) {
console.log('handleSelectionChange', val)
selectItems(val)
// multipleSelection.value = val
}
const getData = async () => { const getData = async () => {
await getCateLists() await getCateLists()
treeRef.value?.setCurrentKey(cateId.value) treeRef.value?.setCurrentKey(cateId.value)
@@ -519,9 +403,16 @@ defineExpose({
<style scoped lang="scss"> <style scoped lang="scss">
.material { .material {
@apply h-full min-h-0 flex flex-1; height: 100%;
min-height: 0px;
display: flex;
flex: 1 1 0%;
&__left { &__left {
@apply border-r border-br flex flex-col w-[200px]; border-right-width: 1px;
border-color: var(--el-border-color);
display: flex;
flex-direction: column;
width: 200px;
:deep(.el-tree-node__content) { :deep(.el-tree-node__content) {
height: 36px; height: 36px;
} }
@@ -537,7 +428,8 @@ defineExpose({
padding: 5px; padding: 5px;
cursor: pointer; cursor: pointer;
&.select { &.select {
@apply text-primary bg-primary-light-8; color: var(--el-color-primary);
background-color: var(--el-color-primary-light-8);
} }
} }
.file-list { .file-list {
@@ -569,7 +461,10 @@ defineExpose({
} }
} }
&__right { &__right {
@apply border-l border-br flex flex-col; border-left-width: 1px;
border-color: var(--el-border-color);
display: flex;
flex-direction: column;
width: 130px; width: 130px;
.select-lists { .select-lists {
padding: 10px; padding: 10px;

View File

@@ -10,6 +10,7 @@
:center="center" :center="center"
:append-to-body="true" :append-to-body="true"
:width="width" :width="width"
draggable
:close-on-click-modal="clickModalClose" :close-on-click-modal="clickModalClose"
@closed="close" @closed="close"
> >

View File

@@ -64,6 +64,7 @@ export class Feedback {
// 确认窗体 // 确认窗体
confirm(msg: string) { confirm(msg: string) {
return ElMessageBox.confirm(msg, '温馨提示', { return ElMessageBox.confirm(msg, '温馨提示', {
draggable: true,
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
@@ -72,6 +73,7 @@ export class Feedback {
// 提交内容 // 提交内容
prompt(content: string, title: string, options?: ElMessageBoxOptions) { prompt(content: string, title: string, options?: ElMessageBoxOptions) {
return ElMessageBox.prompt(content, title, { return ElMessageBox.prompt(content, title, {
draggable: true,
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
...options ...options

View File

@@ -8,13 +8,13 @@
<!-- <el-form-item label="申请人id" prop="applyUserId"> <!-- <el-form-item label="申请人id" prop="applyUserId">
<el-input v-model="queryParams.applyUserId" /> <el-input v-model="queryParams.applyUserId" />
</el-form-item> --> </el-form-item> -->
<el-form-item label="申请人" prop="applyUserNickname"> <el-form-item label="申请人" prop="applyUserNickname" class="w-[280px]">
<el-input v-model="queryParams.applyUserNickname" /> <el-input v-model="queryParams.applyUserNickname" />
</el-form-item> </el-form-item>
<el-form-item label="流程名称" prop="flowName"> <el-form-item label="流程名称" prop="flowName" class="w-[280px]">
<el-input v-model="queryParams.flowName" /> <el-input v-model="queryParams.flowName" />
</el-form-item> </el-form-item>
<el-form-item label="流程分类" prop="flowGroup"> <el-form-item label="流程分类" prop="flowGroup" class="w-[280px]">
<el-select v-model="queryParams.flowGroup" clearable> <el-select v-model="queryParams.flowGroup" clearable>
<el-option label="全部" value="" /> <el-option label="全部" value="" />
<el-option <el-option

View File

@@ -11,10 +11,10 @@
<el-form-item label="申请人昵称" prop="applyUserNickname"> <el-form-item label="申请人昵称" prop="applyUserNickname">
<el-input v-model="queryParams.applyUserNickname" /> <el-input v-model="queryParams.applyUserNickname" />
</el-form-item> --> </el-form-item> -->
<el-form-item label="流程名称" prop="flowName"> <el-form-item label="流程名称" prop="flowName" class="w-[280px]">
<el-input v-model="queryParams.flowName" /> <el-input v-model="queryParams.flowName" />
</el-form-item> </el-form-item>
<el-form-item label="流程分类" prop="flowGroup"> <el-form-item label="流程分类" prop="flowGroup" class="w-[280px]">
<el-select v-model="queryParams.flowGroup" clearable> <el-select v-model="queryParams.flowGroup" clearable>
<el-option label="全部" value="" /> <el-option label="全部" value="" />
<el-option <el-option
@@ -28,7 +28,7 @@
<!-- <el-form-item label="流程描述" prop="flowRemark"> <!-- <el-form-item label="流程描述" prop="flowRemark">
<el-input v-model="queryParams.flowRemark" /> <el-input v-model="queryParams.flowRemark" />
</el-form-item> --> </el-form-item> -->
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status" class="w-[280px]">
<el-select v-model="queryParams.status" clearable> <el-select v-model="queryParams.status" clearable>
<el-option label="全部" value="" /> <el-option label="全部" value="" />
<el-option <el-option

View File

@@ -6,7 +6,15 @@
<el-input v-model="queryParams.flowName" /> <el-input v-model="queryParams.flowName" />
</el-form-item> </el-form-item>
<el-form-item class="w-[280px]" label="流程分类" prop="flowGroup"> <el-form-item class="w-[280px]" label="流程分类" prop="flowGroup">
<el-input v-model="queryParams.flowGroup" /> <el-select v-model="queryParams.flowGroup" clearable>
<el-option label="全部" value="" />
<el-option
v-for="(item, index) in dictData.flow_group"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item class="w-[280px]" label="流程描述" prop="flowRemark"> <el-form-item class="w-[280px]" label="流程描述" prop="flowRemark">
<el-input v-model="queryParams.flowRemark" /> <el-input v-model="queryParams.flowRemark" />

View File

@@ -15,7 +15,7 @@
mode="page" mode="page"
file-size="120px" file-size="120px"
:limit="-1" :limit="-1"
:page-size="20" :page-size="10"
/> />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@@ -24,6 +24,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import material from '@/components/material/index.vue'
defineOptions({ defineOptions({
name: 'materialCenter' name: 'materialCenter'
}) })

View File

@@ -1,7 +1,6 @@
<!-- 系统日志 --> <!-- 系统日志 -->
<template> <template>
<div class="journal"> <div class="journal">
{{ formData }}
<el-card class="!border-none" shadow="never"> <el-card class="!border-none" shadow="never">
<el-form class="ls-form" :model="formData" inline> <el-form class="ls-form" :model="formData" inline>
<el-form-item label="管理员" class="w-[360px]"> <el-form-item label="管理员" class="w-[360px]">

View File

@@ -9,14 +9,14 @@ type CommonUploadImageReq struct {
//CommonAlbumListReq 相册文件列表参数 //CommonAlbumListReq 相册文件列表参数
type CommonAlbumListReq struct { type CommonAlbumListReq struct {
Cid int `form:"cid,default=-1"` // 类目ID Cid int `form:"cid,default=-1"` // 类目ID
Type int `form:"type" binding:"omitempty,oneof=10 20"` // 文件类型: [10=图片, 20=视频] Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 文件类型: [10=图片, 20=视频]
Name string `form:"keyword"` // 文件名称 Name string `form:"name"` // 文件名称
} }
//CommonAlbumRenameReq 相册文件重命名参数 //CommonAlbumRenameReq 相册文件重命名参数
type CommonAlbumRenameReq struct { type CommonAlbumRenameReq struct {
ID uint `form:"id" binding:"required,gt=0"` // 主键 ID uint `form:"id" binding:"required,gt=0"` // 主键
Name string `form:"keyword" binding:"required,min=1,max=30"` // 文件名称 Name string `form:"name" binding:"required,min=1,max=30"` // 文件名称
} }
//CommonAlbumMoveReq 相册文件移动参数 //CommonAlbumMoveReq 相册文件移动参数
@@ -30,7 +30,7 @@ type CommonAlbumAddReq struct {
Cid uint `form:"cid" binding:"gte=0"` // 类目ID Cid uint `form:"cid" binding:"gte=0"` // 类目ID
Aid uint `form:"aid" binding:"gte=0"` // 管理ID Aid uint `form:"aid" binding:"gte=0"` // 管理ID
Uid uint `form:"uid" binding:"gte=0"` // 用户ID Uid uint `form:"uid" binding:"gte=0"` // 用户ID
Type int `form:"type" binding:"oneof=10 20"` // 文件类型: [10=图片, 20=视频] Type int `form:"type" binding:"oneof=10 20 30"` // 文件类型: [10=图片, 20=视频,30文件]
Name string `form:"name"` // 文件名称 Name string `form:"name"` // 文件名称
Uri string `form:"uri"` // 文件路径 Uri string `form:"uri"` // 文件路径
Ext string `form:"ext"` // 文件扩展 Ext string `form:"ext"` // 文件扩展
@@ -44,21 +44,21 @@ type CommonAlbumDelReq struct {
//CommonCateListReq 相册分类列表参数 //CommonCateListReq 相册分类列表参数
type CommonCateListReq struct { type CommonCateListReq struct {
Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 分类类型: [10=图片,20=视频] Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
Name string `form:"keyword"` // 分类名称 Name string `form:"name"` // 分类名称
} }
//CommonCateAddReq 相册分类新增参数 //CommonCateAddReq 相册分类新增参数
type CommonCateAddReq struct { type CommonCateAddReq struct {
Pid uint `form:"pid" binding:"gte=0"` // 父级ID Pid uint `form:"pid" binding:"gte=0"` // 父级ID
Type int `form:"type" binding:"required,oneof=10 20 30"` // 分类类型: [10=图片,20=视频] Type int `form:"type" binding:"required,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
Name string `form:"name" binding:"required,min=1,max=30"` // 分类名称 Name string `form:"name" binding:"required,min=1,max=30"` // 分类名称
} }
//CommonCateRenameReq 相册分类重命名参数 //CommonCateRenameReq 相册分类重命名参数
type CommonCateRenameReq struct { type CommonCateRenameReq struct {
ID uint `form:"id" binding:"required,gt=0"` // 主键 ID uint `form:"id" binding:"required,gt=0"` // 主键
Name string `form:"keyword" binding:"required,min=1,max=30"` // 分类名称 Name string `form:"name" binding:"required,min=1,max=30"` // 分类名称
} }
//CommonCateDelReq 相册分类删除参数 //CommonCateDelReq 相册分类删除参数

View File

@@ -78,10 +78,10 @@ func initServer(router *gin.Engine) *http.Server {
// @title x_admin文档 // @title x_admin文档
// @version 0.0.1 // @version 0.0.1
// @description x_admin是一个完整的后台管理系统 // @description x_admin是一个完整的后台管理系统
// @termsOfService http://likeadmin.adtk.cn // @termsOfService http://x.adtk.cn
// @contact.name API Support // @contact.name API Support
// @contact.url http://likeadmin.adtk.cn // @contact.url http://x.adtk.cn
// @contact.email 11675084@qq.com // @contact.email 11675084@qq.com
// @license.name MIT License // @license.name MIT License