no message

This commit is contained in:
kuaifan 2025-05-15 19:23:34 +08:00
parent 0c34df290e
commit ef696391d8
11 changed files with 127 additions and 99 deletions

View File

@ -81,43 +81,46 @@ class AppsController extends AbstractController
}
/**
* @api {get} api/apps/stats 04. 获取应用状况
* @api {get} api/apps/status 04. 获取应用状态
*
* @apiDescription 获取应用状况,包括已安装的应用和应用入口点
* @apiDescription 获取应用状态,包括已安装的应用和应用菜单
* @apiVersion 1.0.0
* @apiGroup apps
* @apiName stats
* @apiName status
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 应用入口点信息
* @apiSuccess {Object} data 应用和菜单信息
* @apiSuccess {Array} data.installed 已安装应用列表
* @apiSuccess {Array} data.menus 应用菜单列表
*/
public function stats()
public function status()
{
User::auth();
// 获取已安装应用列表
$res = Apps::appList();
if (Base::isError($res)) {
return $res;
}
$installedName = ['appstore'];
foreach ($res['data'] as $app) {
if ($app['config']['status'] == 'installed') {
$installedName[] = $app['name'];
}
$appList = Apps::appList();
if (Base::isSuccess($appList)) {
$installedName = array_merge(
$installedName,
array_column(
array_filter($appList['data'], fn($app) => $app['config']['status'] === 'installed'),
'name'
)
);
}
// 获取应用入口点
$res = Apps::getAppEntryPoints();
if (Base::isError($res)) {
return $res;
// 获取应用菜单
$menusData = [];
$res = Apps::getAppMenuItems();
if (Base::isSuccess($res)) {
$menusData = $res['data'];
}
$entriesData = $res['data'];
return Base::retSuccess('success', [
'installed' => $installedName,
'entries' => $entriesData,
'menus' => $menusData,
]);
}

View File

@ -344,41 +344,41 @@ class Apps
}
/**
* 获取应用的入口点配置
* 获取应用的菜单配置
*
* @param string|null $appName 应用名称为null时获取所有已安装应用的入口点
* @param string|null $appName 应用名称为null时获取所有已安装应用的菜单
* @return array
*/
public static function getAppEntryPoints(?string $appName = null): array
public static function getAppMenuItems(?string $appName = null): array
{
if ($appName !== null) {
return self::entryGetSingle($appName);
return self::menuGetSingle($appName);
}
return self::entryGetAll();
return self::menuGetAll();
}
/**
* 获取单个应用的入口点配置
* 获取单个应用的菜单配置
*
* @param string $appName 应用名称
* @return array
*/
private static function entryGetSingle(string $appName): array
private static function menuGetSingle(string $appName): array
{
$baseDir = base_path('docker/appstore/apps/' . $appName);
$entryPoints = [];
$menuItems = [];
if (!file_exists($baseDir . '/config.yml')) {
return Base::retSuccess("success", $entryPoints);
return Base::retSuccess("success", $menuItems);
}
try {
$configData = Yaml::parseFile($baseDir . '/config.yml');
if (isset($configData['entry_points']) && is_array($configData['entry_points'])) {
foreach ($configData['entry_points'] as $entry) {
$normalizedEntry = self::entryNormalize($entry, $appName);
if ($normalizedEntry) {
$entryPoints[] = $normalizedEntry;
if (isset($configData['menu_items']) && is_array($configData['menu_items'])) {
foreach ($configData['menu_items'] as $menu) {
$normalizedMenu = self::menuNormalize($menu, $appName);
if ($normalizedMenu) {
$menuItems[] = $normalizedMenu;
}
}
}
@ -386,21 +386,21 @@ class Apps
return Base::retError('配置文件解析失败:' . $e->getMessage());
}
return Base::retSuccess("success", $entryPoints);
return Base::retSuccess("success", $menuItems);
}
/**
* 获取所有已安装应用的入口点配置
* 获取所有已安装应用的菜单配置
*
* @return array
*/
private static function entryGetAll(): array
private static function menuGetAll(): array
{
$allEntryPoints = [];
$allMenuItems = [];
$baseDir = base_path('docker/appstore/apps');
if (!is_dir($baseDir)) {
return Base::retSuccess("success", $allEntryPoints);
return Base::retSuccess("success", $allMenuItems);
}
$dirs = scandir($baseDir);
@ -413,48 +413,48 @@ class Apps
continue;
}
$appEntryPoints = self::entryGetSingle($dir);
if (Base::isSuccess($appEntryPoints)) {
$allEntryPoints = array_merge($allEntryPoints, $appEntryPoints['data']);
$appMenuItems = self::menuGetSingle($dir);
if (Base::isSuccess($appMenuItems)) {
$allMenuItems = array_merge($allMenuItems, $appMenuItems['data']);
}
}
return Base::retSuccess("success", $allEntryPoints);
return Base::retSuccess("success", $allMenuItems);
}
/**
* 标准化入口点配置
* 标准化菜单配置
*
* @param array $entry 原始入口点配置
* @param array $menu 原始菜单配置
* @param string $appName 应用名称
* @return array|null 标准化后的入口点配置配置无效时返回null
* @return array|null 标准化后的菜单配置配置无效时返回null
*/
private static function entryNormalize(array $entry, string $appName): ?array
private static function menuNormalize(array $menu, string $appName): ?array
{
// 检查必需的字段
if (!isset($entry['location']) || !isset($entry['url'])) {
if (!isset($menu['location']) || !isset($menu['url'])) {
return null;
}
// 基础配置
$normalizedEntry = [
$normalizedMenu = [
'app_name' => $appName,
'location' => $entry['location'],
'url' => $entry['url'],
'key' => $entry['key'] ?? substr(md5($entry['url']), 0, 16),
'icon' => self::processAppIcon($appName, [$entry['icon'] ?? '']),
'label' => self::getMultiLanguageField($entry['label'] ?? ''),
'location' => $menu['location'],
'url' => $menu['url'],
'key' => $menu['key'] ?? substr(md5($menu['url']), 0, 16),
'icon' => self::processAppIcon($appName, [$menu['icon'] ?? '']),
'label' => self::getMultiLanguageField($menu['label'] ?? ''),
];
// 处理可选的UI配置
$optionalConfigs = ['transparent', 'keepAlive'];
foreach ($optionalConfigs as $config) {
if (isset($entry[$config])) {
$normalizedEntry[$config] = $entry[$config];
if (isset($menu[$config])) {
$normalizedMenu[$config] = $menu[$config];
}
}
return $normalizedEntry;
return $normalizedMenu;
}
/**
@ -830,7 +830,7 @@ class Apps
// 处理应用名称
$appName = Base::camel2snake(Base::cn2pinyin($configData['name'], '_'));
if (in_array($appName, self::$protectedServiceNames)) {
return Base::retError('服务名称 "' . $name . '" 被保护,不能使用');
return Base::retError('服务名称 "' . $appName . '" 被保护,不能使用');
}
$targetDir = base_path('docker/appstore/apps/' . $appName);
$targetConfigFile = $targetDir . '/config.json';

View File

@ -424,7 +424,7 @@ export default {
microApp.forceSetData(name, {type: 'beforeClose'}, array => {
if (!array?.find(item => item === true)) {
if (name === 'appstore') {
this.$store.dispatch("updateMicroAppsStats");
this.$store.dispatch("updateMicroAppsStatus");
}
if ($A.isSubElectron) {
$A.Electron.sendMessage('windowDestroy');

View File

@ -120,6 +120,10 @@
<div class="menu-title">{{$L('应用')}}</div>
<Badge class="menu-badge" :overflow-count="999" :text="String((reportUnreadNumber + approveUnreadNumber) || '')"/>
</li>
<li v-for="(item, key) in filterMicroAppsMenusMain" :key="key" @click="onTabbarClick('microApp', item)">
<div class="apply-icon no-dark-content" :style="{backgroundImage: `url(${item.icon})`}"></div>
<div class="menu-title">{{item.label}}</div>
</li>
</ul>
</div>
<div ref="menuProject" class="menu-project">
@ -515,7 +519,7 @@ export default {
'longpressData',
]),
...mapGetters(['dashboardTask']),
...mapGetters(['dashboardTask', "filterMicroAppsMenusMain"]),
/**
* page className
@ -1167,7 +1171,7 @@ export default {
})
},
onTabbarClick(act) {
onTabbarClick(act, params = '') {
switch (act) {
case 'createGroup':
this.onAddMenu('group')
@ -1183,6 +1187,9 @@ export default {
case 'workReport':
this.settingRoute(act)
break;
case 'microApp':
this.$store.dispatch("openMicroApp", params);
break;
case 'appstore':
this.$store.dispatch("openMicroApp", {
name: 'appstore',

View File

@ -15,7 +15,7 @@
{{ t == 'base' ? $L('常用') : $L('管理员') }}
</div>
<Row :gutter="16">
<Col v-for="item in (t == 'base' ? filterMicroAppsEntries : filterMicroAppsEntriesAdmin)" :key="item.key"
<Col v-for="(item, key) in (t == 'base' ? filterMicroAppsMenus : filterMicroAppsMenusAdmin)" :key="key"
:xs="{ span: 6 }"
:sm="{ span: 6 }"
:lg="{ span: 6 }"
@ -26,7 +26,7 @@
<div class="logo">
<div class="apply-icon no-dark-content" :style="{backgroundImage: `url(${item.icon})`}"></div>
</div>
<p>{{ $L(item.label) }}</p>
<p>{{ item.label }}</p>
</div>
</div>
</Col>
@ -364,6 +364,7 @@ export default {
},
activated() {
this.initList()
this.$store.dispatch("updateMicroAppsStatus")
},
computed: {
...mapState([
@ -378,8 +379,8 @@ export default {
'routeLoading',
]),
...mapGetters([
'filterMicroAppsEntries',
'filterMicroAppsEntriesAdmin',
'filterMicroAppsMenus',
'filterMicroAppsMenusAdmin',
]),
isExistAdminList() {
return this.applyList.map(h => h.type).indexOf('admin') !== -1;
@ -449,11 +450,8 @@ export default {
return item.value == type && num > 0
},
//
applyClick(item, area = '') {
applyClick(item, params = '') {
switch (item.value) {
case 'microApp':
this.$store.dispatch("openMicroApp", area);
return
case 'approve':
case 'calendar':
case 'file':
@ -461,27 +459,27 @@ export default {
this.goForward({ name: 'manage-' + item.value });
break;
case 'report':
emitter.emit('openReport', area == 'badge' ? 'receive' : 'my');
emitter.emit('openReport', params == 'badge' ? 'receive' : 'my');
break;
case 'mybot':
this.getMybot();
this.mybotShow = true;
break;
case 'mybot-chat':
this.chatMybot(area.id);
this.chatMybot(params.id);
break;
case 'mybot-add':
this.addMybot(area);
this.addMybot(params);
break;
case 'mybot-del':
this.delMybot(area);
this.delMybot(params);
break;
case 'robot':
this.getAITags();
this.aibotShow = true;
break;
case 'robot-setting':
this.aibotTabAction = area;
this.aibotTabAction = params;
this.aibotSettingShow = true;
break;
case 'signin':
@ -510,7 +508,7 @@ export default {
break;
}
this.$emit("on-click", item.value)
this.$emit("on-click", item.value, params);
},
//
getMybot() {

View File

@ -2619,7 +2619,7 @@ export default {
});
},
openOkr(id) {
openOkrDetails(id) {
if (!id) {
return;
}
@ -3150,7 +3150,7 @@ export default {
break;
case 'okr':
this.openOkr(this.dialogData.link_id)
this.openOkrDetails(this.dialogData.link_id)
break;
default:
@ -3758,7 +3758,7 @@ export default {
this.$store.dispatch("openTask", $A.runNum(target.getAttribute("data-id")));
}
if (target.classList.contains('mention') && target.classList.contains('okr')) {
this.openOkr($A.runNum(target.getAttribute("data-id")));
this.openOkrDetails($A.runNum(target.getAttribute("data-id")));
}
break;

View File

@ -623,7 +623,7 @@ export default {
dispatch("getProjectByQueue");
dispatch("getTaskForDashboard");
dispatch("dialogMsgRead");
dispatch("updateMicroAppsStats");
dispatch("updateMicroAppsStatus");
//
const allIds = Object.values(state.userAvatar).map(({userid}) => userid);
[...new Set(allIds)].some(userid => dispatch("getUserBasic", {userid}))
@ -1116,6 +1116,8 @@ export default {
'callAt',
'cacheEmojis',
'cacheDialogs',
'microAppsInstalled',
'microAppsMenus',
],
json: [
'userInfo'
@ -4640,17 +4642,16 @@ export default {
/** *****************************************************************************************/
/**
* 更新微应用状已安装入口菜单
* @param state
* 更新微应用状已安装应用菜单项
* @param commit
* @param dispatch
* @param appName
*/
updateMicroAppsStats({state, dispatch}) {
updateMicroAppsStatus({commit, dispatch}) {
dispatch("call", {
url: 'apps/stats',
url: 'apps/status',
}).then(({data}) => {
state.microAppsInstalled = data.installed
state.microAppsEntries = data.entries
commit("microApps/installed", data.installed)
commit("microApps/menu", data.menus)
})
},

View File

@ -279,35 +279,35 @@ export default {
},
/**
* 获取应用菜单入口
* 获取应用菜单
* 过滤出location为application的菜单项
*
* @param {Object} state
* @returns {Array}
* @returns {Array}
*/
filterMicroAppsEntries: (state) => {
return state.microAppsEntries.filter(item => item.location === 'application')
filterMicroAppsMenus: (state) => {
return state.microAppsMenus.filter(item => item.location === 'application')
},
/**
* 获取应用管理菜单入口
* 获取应用管理菜单
* 过滤出location为application/admin的菜单项
*
* @param {Object} state
* @returns {Array}
* @returns {Array}
*/
filterMicroAppsEntriesAdmin: (state) => {
return state.microAppsEntries.filter(item => item.location === 'application/admin')
filterMicroAppsMenusAdmin: (state) => {
return state.microAppsMenus.filter(item => item.location === 'application/admin')
},
/**
* 获取主导航菜单入口
* 获取主导航菜单
* 过滤出location为main/menu的菜单项
*
* @param {Object} state
* @returns {Array}
* @returns {Array}
*/
filterMicroAppsEntriesMain: (state) => {
return state.microAppsEntries.filter(item => item.location === 'main/menu')
filterMicroAppsMenusMain: (state) => {
return state.microAppsMenus.filter(item => item.location === 'main/menu')
}
}

View File

@ -298,4 +298,15 @@ export default {
'menu/operation': function(state, data) {
state.menuOperation = data || {}
},
// 微应用管理
'microApps/menu': function(state, data) {
state.microAppsMenus = data
$A.IDBSave("microAppsMenus", state.microAppsMenus)
},
'microApps/installed': function(state, data) {
state.microAppsInstalled = data
$A.IDBSave("microAppsInstalled", state.microAppsInstalled)
},
}

View File

@ -261,7 +261,7 @@ export default {
// 长按数据
longpressData: {type: '', data: null, element: null},
// 微应用菜单入口
// 微应用数据
microAppsInstalled: [],
microAppsEntries: [],
microAppsMenus: [],
};

View File

@ -55,6 +55,14 @@
font-size: 20px;
margin-right: 10px;
}
.apply-icon {
width: 20px;
height: 20px;
background-repeat: no-repeat;
background-size: contain;
background-position: center center;
margin-right: 10px;
}
.menu-title {
flex: 1;
white-space: nowrap;