新增帮助文档管理

This commit is contained in:
六如
2025-06-15 15:27:43 +08:00
parent c18ee0dff9
commit 4d70c5167d
36 changed files with 2649 additions and 5 deletions

View File

@@ -0,0 +1,82 @@
package com.gitee.sop.admin.dao.entity;
import java.time.LocalDateTime;
import com.gitee.fastmybatis.annotation.Column;
import com.gitee.fastmybatis.annotation.Pk;
import com.gitee.fastmybatis.annotation.PkStrategy;
import com.gitee.fastmybatis.annotation.Table;
import lombok.Data;
/**
* 表名help_doc
* 备注:帮助内容表
*
* @author thc
*/
@Table(name = "help_doc", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
@Data
public class HelpDoc {
/**
* id
*/
private Long id;
/**
* 文档名称
*/
private String label;
/**
* 排序
*/
private Integer sort;
@Column(logicDelete = true)
private Integer isDeleted;
/**
* 状态1启用2禁用
*/
private Integer status;
/**
* 内容
*/
private String content;
/**
* 内容类型,1-Markdown,2-富文本
*/
private Integer contentType;
/**
* 父级id
*/
private Long parentId;
/**
* 添加时间
*/
private LocalDateTime addTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
/**
* 创建人id
*/
private Long addBy;
/**
* 修改人id
*/
private Long updateBy;
}

View File

@@ -0,0 +1,13 @@
package com.gitee.sop.admin.dao.mapper;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import com.gitee.sop.admin.dao.entity.HelpDoc;
/**
* @author thc
*/
@Mapper
public interface HelpDocMapper extends BaseMapper<HelpDoc> {
}

View File

@@ -0,0 +1,55 @@
package com.gitee.sop.admin.service.help;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.sop.admin.common.dto.StatusUpdateDTO;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.dao.entity.HelpDoc;
import com.gitee.sop.admin.dao.mapper.HelpDocMapper;
import com.gitee.sop.admin.service.help.dto.HelpDocDTO;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author thc
*/
@Service
public class HelpDocService implements ServiceSupport<HelpDoc, HelpDocMapper> {
public List<HelpDocDTO> listTree() {
return this.query()
.select(HelpDoc::getId, HelpDoc::getLabel, HelpDoc::getParentId,
HelpDoc::getAddTime,
HelpDoc::getUpdateTime)
.orderByAsc(HelpDoc::getSort)
.list(data -> CopyUtil.copyBean(data, HelpDocDTO::new));
}
public PageInfo<HelpDoc> doPage(LambdaQuery<HelpDoc> query) {
query.orderByDesc(HelpDoc::getId);
PageInfo<HelpDoc> page = this.page(query);
// 格式转换
return page.convert(isvInfo -> {
return isvInfo;
});
}
/**
* 修改状态
*
* @param statusUpdateDTO 修改值
* @return 返回影响行数
*/
public int updateStatus(StatusUpdateDTO statusUpdateDTO) {
return this.query()
.eq(HelpDoc::getId, statusUpdateDTO.getId())
.set(HelpDoc::getStatus, statusUpdateDTO.getStatus())
.update();
}
}

View File

@@ -0,0 +1,66 @@
package com.gitee.sop.admin.service.help.dto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class HelpDocDTO {
/**
* 主键ID唯一标识每条帮助文档记录
*/
private Long id;
/**
* 文档名称,显示用的标题或标签
*/
private String label;
/**
* 排序字段,用于控制文档在列表中的显示顺序
*/
private Integer sort;
/**
* 状态字段,表示文档是否启用
* 1启用2禁用
*/
private Byte status;
/**
* 内容字段,存储文档的具体内容
*/
private String content;
/**
* 内容类型,区分内容的格式
* 1Markdown2富文本
*/
private Byte contentType;
/**
* 父级ID用于构建文档的层级结构
*/
private Long parentId;
/**
* 添加时间,记录文档首次创建的时间
*/
private LocalDateTime addTime;
/**
* 更新时间,记录文档最后一次修改的时间
*/
private LocalDateTime updateTime;
/**
* 创建人ID记录创建该文档的用户ID
*/
private Long addBy;
/**
* 修改人ID记录最后一次修改该文档的用户ID
*/
private Long updateBy;
}

View File

@@ -0,0 +1,45 @@
package com.gitee.sop.admin.controller.doc.param;
import lombok.Data;
/**
* 新增表单
*
* @author thc
*/
@Data
public class HelpDocAddParam {
/**
* 文档名称
*/
private String label;
/**
* 排序
*/
private Integer sort;
/**
* 状态1启用2禁用
*/
private Integer status;
/**
* 内容
*/
private String content;
/**
* 内容类型,1-Markdown,2-富文本
*/
private Integer contentType;
/**
* 父级id
*/
private Long parentId;
}

View File

@@ -0,0 +1,82 @@
package com.gitee.sop.admin.controller.doc.param;
import java.time.LocalDateTime;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 查询表单
*
* @author thc
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class HelpDocSearchParam extends PageParam {
private static final long serialVersionUID = 1L;
/**
* 文档名称
*/
@Condition(operator = Operator.like)
private String label;
/**
* 排序
*/
@Condition
private Integer sort;
/**
* 状态1启用2禁用
*/
@Condition
private Integer status;
/**
* 内容
*/
@Condition(operator = Operator.like)
private String content;
/**
* 内容类型,1-Markdown,2-富文本
*/
@Condition
private Integer contentType;
/**
* 父级id
*/
@Condition
private Long parentId;
/**
* 添加时间
*/
@Condition
private LocalDateTime addTime;
/**
* 修改时间
*/
@Condition
private LocalDateTime updateTime;
/**
* 创建人id
*/
@Condition
private Long addBy;
/**
* 修改人id
*/
@Condition
private Long updateBy;
}

View File

@@ -0,0 +1,24 @@
package com.gitee.sop.admin.controller.doc.param;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
/**
* 修改表单
*
* @author thc
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class HelpDocUpdateParam extends HelpDocAddParam {
/**
* id
*/
@NotNull(message = "id必填")
private Long id;
}

View File

@@ -0,0 +1,114 @@
package com.gitee.sop.admin.controller.help;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.sop.admin.common.req.IdParam;
import com.gitee.sop.admin.common.resp.Result;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.controller.doc.param.HelpDocAddParam;
import com.gitee.sop.admin.controller.doc.param.HelpDocSearchParam;
import com.gitee.sop.admin.controller.doc.param.HelpDocUpdateParam;
import com.gitee.sop.admin.controller.help.vo.HelpDocVO;
import com.gitee.sop.admin.dao.entity.HelpDoc;
import com.gitee.sop.admin.service.help.HelpDocService;
import com.gitee.sop.admin.service.help.dto.HelpDocDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* HelpDoc
*
* @author thc
*/
@RestController
@RequestMapping("help/doc")
public class HelpDocController {
@Autowired
private HelpDocService helpDocService;
/**
* 查询帮助文档树状结构
*
* @return
*/
@GetMapping("tree")
public Result<List<HelpDocVO>> listTree() {
List<HelpDocDTO> list = helpDocService.listTree();
List<HelpDocVO> retList = CopyUtil.copyList(list, HelpDocVO::new);
return Result.ok(retList);
}
/**
* 查询帮助文档
*
* @return
*/
@GetMapping("detail")
public Result<HelpDocVO> doc(Long id) {
HelpDoc helpDoc = helpDocService.getById(id);
HelpDocVO helpDocVO = CopyUtil.copyBean(helpDoc, HelpDocVO::new);
return Result.ok(helpDocVO);
}
/**
* 分页查询
*
* @param param 查询参数
* @return 返回分页结果
*/
@GetMapping("/page")
public Result<PageInfo<HelpDocVO>> page(HelpDocSearchParam param) {
LambdaQuery<HelpDoc> query = param.toLambdaQuery(HelpDoc.class);
PageInfo<HelpDocVO> pageInfo = helpDocService.doPage(query)
.convert(data -> CopyUtil.copyBean(data, HelpDocVO::new));
return Result.ok(pageInfo);
}
/**
* 新增记录
*
* @param param 表单参数
* @return 返回添加后的主键值
*/
@PostMapping("/add")
public Result<Long> add(@Validated @RequestBody HelpDocAddParam param) {
HelpDoc helpDoc = CopyUtil.copyBean(param, HelpDoc::new);
helpDocService.save(helpDoc);
// 返回添加后的主键值
return Result.ok(helpDoc.getId());
}
/**
* 修改记录
*
* @param param 表单数据
* @return 返回影响行数
*/
@PostMapping("/update")
public Result<Integer> update(@Validated @RequestBody HelpDocUpdateParam param) {
HelpDoc helpDoc = CopyUtil.copyBean(param, HelpDoc::new);
return Result.ok(helpDocService.update(helpDoc));
}
/**
* 删除记录
*
* @param param 参数
* @return 返回影响行数
*/
@PostMapping("/delete")
public Result<Integer> delete(@Validated @RequestBody IdParam param) {
return Result.ok(helpDocService.deleteById(param.getId()));
}
}

View File

@@ -0,0 +1,75 @@
package com.gitee.sop.admin.controller.help.vo;
import java.time.LocalDateTime;
import com.gitee.sop.admin.service.jackson.convert.annotation.UserFormat;
import lombok.Data;
/**
* 返回结果
*
* @author thc
*/
@Data
public class HelpDocVO {
/**
* id
*/
private Long id;
/**
* 文档名称
*/
private String label;
/**
* 排序
*/
private Integer sort;
/**
* 状态1启用2禁用
*/
private Integer status;
/**
* 内容
*/
private String content = "";
/**
* 内容类型,1-Markdown,2-富文本
*/
private Integer contentType;
/**
* 父级id
*/
private Long parentId;
/**
* 添加时间
*/
private LocalDateTime addTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
/**
* 创建人id
*/
@UserFormat
private Long addBy;
/**
* 修改人id
*/
@UserFormat
private Long updateBy;
}

View File

@@ -48,6 +48,10 @@
"url": "https://github.com/xiaoxian521"
},
"dependencies": {
"@bytemd/plugin-gemoji": "1.21.0",
"@bytemd/plugin-gfm": "1.21.0",
"@bytemd/plugin-highlight": "1.21.0",
"@bytemd/vue-next": "1.21.0",
"@element-plus/icons-vue": "^2.3.1",
"@pureadmin/descriptions": "1.2.1",
"@pureadmin/table": "3.2.0",
@@ -60,6 +64,8 @@
"dayjs": "1.11.12",
"echarts": "5.5.1",
"element-plus": "2.8.0",
"github-markdown-css": "5.8.1",
"highlight.js": "11.10.0",
"js-cookie": "3.0.5",
"localforage": "1.10.0",
"mitt": "3.0.1",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
import { createUrl, http } from "@/utils/http";
import type { Result } from "@/model";
// 后端请求接口
const apiUrl: any = createUrl({
listHelpTree: "/help/doc/tree",
getDoc: "/help/doc/detail",
save: "/help/doc/add",
update: "/help/doc/update",
del: "/help/doc/delete"
});
export interface HelpDoc {
id: number;
label: string;
parentId: number;
content: string;
}
/**
* 接口管理
*/
export const api: any = {
/**
* 查询帮助树
* @param data
*/
listHelpTree(params: object) {
return http.get<Result<Array<HelpDoc>>, any>(apiUrl.listHelpTree, {
params
});
},
/**
* 文档详情
* @param data
*/
getDoc(id: any) {
return http.get<Result<any>, any>(apiUrl.getDoc, {
params: {
id
}
});
},
save(data) {
return http.post(apiUrl.save, {
data
});
},
update(data) {
return http.post(apiUrl.update, {
data
});
},
del(data) {
return http.post(apiUrl.del, {
data
});
}
};

View File

@@ -0,0 +1,9 @@
import { ref } from "vue";
import mdView from "./index.vue";
import { withInstall } from "@pureadmin/utils";
const MarkdownEditor = withInstall(mdView);
export const updateValue = ref("");
export default MarkdownEditor;

View File

@@ -0,0 +1,93 @@
<script setup lang="ts">
// bytemd https://github.com/bytedance/bytemd.git
import { Editor, Viewer } from "@bytemd/vue-next";
import gfm from "@bytemd/plugin-gfm";
import highlight from "@bytemd/plugin-highlight";
import gemoji from "@bytemd/plugin-gemoji";
import "bytemd/dist/index.css";
import "highlight.js/styles/idea.css";
import "github-markdown-css/github-markdown.css";
import { computed, ref, watch } from "vue";
import { updateValue } from "./index";
const plugins = [gfm(), highlight(), gemoji()];
const props = defineProps({
value: {
type: String,
default: "",
required: true
},
placeholder: {
type: String,
default: "请输入内容支持Markdown语法",
required: false
},
readonly: {
type: Boolean,
default: false
}
});
// 监听props.value的变化并同步到updateValue
watch(
() => props.value,
newVal => {
updateValue.value = newVal;
},
{ immediate: true }
);
const isEdit = computed(() => {
return !props.readonly;
});
function handleChange(v) {
updateValue.value = v;
}
</script>
<template>
<div>
<Editor
v-if="isEdit"
:value="updateValue"
:placeholder="placeholder"
:plugins="plugins"
@change="handleChange"
/>
<Viewer v-else :value="updateValue" :plugins="plugins" />
</div>
</template>
<style lang="scss">
.bytemd {
height: calc(100vh - 100px);
}
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 25px;
ol {
padding-left: 1em;
margin: 0;
list-style: decimal !important;
}
ul {
padding-left: 1em;
margin: 0;
list-style: disc !important;
}
}
.markdown-body p,
.markdown-body blockquote,
.markdown-body ul,
.markdown-body ol,
.markdown-body dl,
.markdown-body table,
.markdown-body pre,
.markdown-body details {
margin-top: 0;
margin-bottom: 16px;
}
</style>

View File

@@ -0,0 +1,91 @@
<template>
<template v-for="item in rootItems" :key="item.id">
<el-sub-menu v-if="hasChildren(item.id)" :index="item.id.toString()">
<template #title>
<el-icon><Document /></el-icon>
<span>{{ item.label }}</span>
<el-icon class="add-icon" @click.stop="handleAdd(item)">
<CirclePlusFilled />
</el-icon>
</template>
<template v-for="child in getChildren(item.id)" :key="child.id">
<el-sub-menu v-if="hasChildren(child.id)" :index="child.id.toString()">
<template #title>
<el-icon><Document /></el-icon>
<span>{{ child.label }}</span>
<el-icon class="add-icon" @click.stop="handleAdd(child)">
<CirclePlusFilled />
</el-icon>
</template>
<el-menu-item
v-for="grandChild in getChildren(child.id)"
:key="grandChild.id"
:index="grandChild.id.toString()"
>
<el-icon><Document /></el-icon>
<span>{{ grandChild.label }}</span>
</el-menu-item>
</el-sub-menu>
<el-menu-item v-else :index="child.id.toString()">
<el-icon><Document /></el-icon>
<span>{{ child.label }}</span>
<el-icon class="add-icon" @click.stop="handleAdd(child)">
<CirclePlusFilled />
</el-icon>
</el-menu-item>
</template>
</el-sub-menu>
<el-menu-item v-else :index="item.id.toString()">
<el-icon><Document /></el-icon>
<span>{{ item.label }}</span>
<el-icon class="add-icon" @click.stop="handleAdd(item)">
<CirclePlusFilled />
</el-icon>
</el-menu-item>
</template>
</template>
<script lang="ts">
import { defineComponent, PropType, computed } from "vue";
import type { HelpDoc } from "@/api/help";
import { Document } from "@element-plus/icons-vue";
export default defineComponent({
name: "RecursiveMenu",
components: {
Document
},
props: {
items: {
type: Array as PropType<HelpDoc[]>,
required: true
},
handleAdd: {
type: Function,
required: false
}
},
setup(props) {
const rootItems = computed(() => {
return props.items.filter(item => item.parentId === 0);
});
const getChildren = (parentId: number) => {
return props.items.filter(item => item.parentId === parentId);
};
const hasChildren = (parentId: number) => {
return props.items.some(item => item.parentId === parentId);
};
return {
rootItems,
getChildren,
hasChildren
};
}
});
</script>
<style scoped>
.el-sub-menu :deep(.add-icon),
.el-menu-item :deep(.add-icon) {
margin-left: 10px;
}
</style>

View File

@@ -35,3 +35,9 @@
margin: 0 !important;
}
}
span.split {
margin-left: 10px;
margin-right: 10px;
color: gray;
}

View File

@@ -0,0 +1,86 @@
import { api, type HelpDoc } from "@/api/help";
import { ElMessage } from "element-plus";
import { onMounted, ref } from "vue";
export function useHelp() {
const menuItems = ref<Array<HelpDoc>>([]);
const docInfo = ref({
id: undefined,
label: "",
content: "",
status: 1,
sort: 1,
parentId: 0
});
function initForm() {
docInfo.value = {
id: undefined,
label: "",
content: "",
status: 1,
parentId: 0,
sort: 1
};
}
function resetForm(pid) {
docInfo.value = {
id: 0,
label: "",
content: "",
parentId: pid || 0,
status: 1,
sort: 1
};
}
function save() {
if (docInfo.value.id === 0) {
api.save(docInfo.value).then(() => {
ElMessage.success("添加成功");
getMenuItems();
});
} else {
api.update(docInfo.value).then(() => {
ElMessage.success("修改成功");
getMenuItems();
});
}
}
function del() {
api.del(docInfo.value).then(() => {
ElMessage.success("删除成功");
initForm();
getMenuItems();
});
}
const getMenuItems = async () => {
const res = await api.listHelpTree({});
// console.log(res);
menuItems.value = res.data;
};
const loadDocData = async (id: any) => {
const res = await api.getDoc(id);
// console.log(res);
const data = res.data;
docInfo.value = data;
return data;
};
onMounted(async () => {
await getMenuItems();
});
return {
menuItems,
loadDocData,
docInfo,
save,
del,
resetForm
};
}

View File

@@ -0,0 +1,130 @@
<template>
<el-container>
<el-aside width="300px" style="margin: 5px">
<el-button type="primary" style="margin-bottom: 10px" @click="handleAdd">
<el-icon><plus /></el-icon>
添加文档
</el-button>
<el-menu default-active="1" class="help-menu" @select="handleSelect">
<recursive-menu :items="menuItems" :handle-add="handleAddChild" />
</el-menu>
</el-aside>
<el-main v-show="showEditor">
<el-form
ref="ruleFormRef"
:model="docInfo"
:rules="rules"
class="help-content"
>
<el-form-item>
<el-button type="primary" @click="handleSave(ruleFormRef)">
保存
</el-button>
<el-button type="danger" @click="handleDel">删除</el-button>
<span class="split">|</span>
<el-switch
v-model="docInfo.status"
class="ml-2"
inline-prompt
style="
--el-switch-on-color: #13ce66;
--el-switch-off-color: #ff4949;
"
active-text="启用"
inactive-text="禁用"
:active-value="1"
:inactive-value="2"
/>
<span class="split">|</span>
排序值<el-input-number
v-model="docInfo.sort"
placeholder="排序值"
/>
</el-form-item>
<el-form-item prop="label">
<el-input v-model="docInfo.label" placeholder="标题" />
</el-form-item>
<MarkdownEditor :value="docInfo.content" />
</el-form>
</el-main>
</el-container>
</template>
<script setup lang="ts">
import { QuestionFilled } from "@element-plus/icons-vue";
import { useHelp } from "@/views/help/index";
import RecursiveMenu from "@/components/RecursiveMenu.vue";
import { computed, ref } from "vue";
import MarkdownEditor from "@/components/MarkdownEditor";
import { updateValue } from "@/components/MarkdownEditor/index";
import { ElMessageBox, FormInstance } from "element-plus";
const { menuItems, loadDocData, docInfo, save, del, resetForm } = useHelp();
const ruleFormRef = ref<FormInstance>();
const rules = {
label: [
{
required: true,
message: "输入标题",
trigger: "blur"
}
]
};
// 根据docInfo的label和content是否存在来判断是否显示编辑器
const showEditor = computed(() => {
return docInfo.value.id !== undefined;
});
const handleAdd = () => {
resetForm(0);
};
const handleAddChild = item => {
resetForm(item.id);
};
const handleSelect = async (key: string) => {
await loadDocData(key);
};
const handleSave = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(valid => {
if (valid) {
docInfo.value.content = updateValue.value;
save();
} else {
console.log("error submit!");
}
});
};
const handleDel = () => {
ElMessageBox.confirm("确定要删除吗?", "提示", {
confirmButtonText: "OK",
cancelButtonText: "Cancel",
type: "warning"
})
.then(() => {
del();
})
.catch(() => {});
};
</script>
<style scoped>
.help-menu {
height: 100%;
border-right: solid 1px var(--el-menu-border-color);
}
.el-aside {
background-color: var(--el-menu-bg-color);
}
.el-main {
padding: 20px;
}
</style>

Binary file not shown.

View File

@@ -0,0 +1,16 @@
package com.gitee.gen;
import org.junit.Test;
/**
* 文件生成到本地项目中
*
* @author 六如
*/
public class GenHelpDocTest extends GenTest {
@Test
public void run() {
this.gen("help_doc.properties");
}
}

View File

@@ -0,0 +1,33 @@
db.name=sop
db.driverClass=com.mysql.cj.jdbc.Driver
db.host=localhost
db.port=3306
db.username=root
db.password=root
# \u4F5C\u8005
author=thc
# \u8868\u540D\uFF0C\u591A\u4E2A\u7528,\u9694\u5F00
tables=help_doc
# \u6A21\u5757\u540D\u79F0
moduleName=doc
# \u5305\u540D
entityPackage=com.gitee.sop.admin.dao.entity
mapperPackage=com.gitee.sop.admin.dao.mapper
servicePackage=com.gitee.sop.admin.service
controllerPackage=com.gitee.sop.admin.controller
# \u4FDD\u5B58\u76EE\u5F55\uFF0C\u586B\u7EDD\u5BF9\u5730\u5740
entityDir=/Users/thc/dev/projects/SOP/sop-admin/sop-admin-backend/admin-dao/src/main/java/com/gitee/sop/admin/dao/entity
mapperDir=/Users/thc/dev/projects/SOP/sop-admin/sop-admin-backend/admin-dao/src/main/java/com/gitee/sop/admin/dao/mapper
serviceDir=/Users/thc/dev/projects/SOP/sop-admin/sop-admin-backend/admin-service/src/main/java/com/gitee/sop/admin/service
controllerDir=/Users/thc/dev/projects/SOP/sop-admin/sop-admin-backend/admin-web/src/main/java/com/gitee/sop/admin/controller
# \u524D\u7AEF\u9879\u76EE\u6839\u76EE\u5F55
frontendRootDir=/Users/thc/dev/projects/SOP/sop-admin/sop-admin-frontend
# \u6307\u5B9A\u6A21\u677F\u5206\u7EC4\u540D\u79F0\uFF0C\u4E00\u822C\u60C5\u51B5\u4E0B\u4E0D\u7528\u6539
groupName=default