1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: 增加应用详情页

This commit is contained in:
zhengkunwang223 2022-09-23 16:33:55 +08:00 committed by zhengkunwang223
parent 367623293c
commit da85e416ea
21 changed files with 299 additions and 196 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant"
"github.com/gin-gonic/gin"
"strconv"
)
func (b *BaseApi) AppSearch(c *gin.Context) {
@ -30,3 +31,33 @@ func (b *BaseApi) AppSync(c *gin.Context) {
}
helper.SuccessWithData(c, "")
}
func (b *BaseApi) GetApp(c *gin.Context) {
idStr := c.Param("id")
u64, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
appDTO, err := appService.GetApp(uint(u64))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, appDTO)
}
func (b *BaseApi) GetAppDetail(c *gin.Context) {
idStr := c.Param("appid")
u64, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
version := c.Param("version")
appDetailDTO, err := appService.GetAppDetail(uint(u64), version)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, appDetailDTO)
}

View File

@ -12,7 +12,12 @@ type AppRes struct {
type AppDTO struct {
model.App
Tags []model.Tag `json:"tags"`
Versions []string `json:"versions"`
Tags []model.Tag `json:"tags"`
}
type AppDetailDTO struct {
model.AppDetail
}
type AppList struct {
@ -53,6 +58,6 @@ type AppFormFields struct {
type AppRequest struct {
PageInfo
Name string `json:"name"`
Types []string `json:"types"`
Name string `json:"name"`
Tags []string `json:"tags"`
}

View File

@ -3,7 +3,7 @@ package model
import "time"
type BaseModel struct {
ID uint `gorm:"primarykey;AUTO_INCREMENT"`
CreatedAt time.Time
UpdatedAt time.Time
ID uint `gorm:"primarykey;AUTO_INCREMENT" json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}

View File

@ -11,12 +11,6 @@ import (
type AppRepo struct {
}
func (a AppRepo) WithInTypes(types []string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("type in (?)", types)
}
}
func (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, error) {
var apps []model.App
db := global.DB.Model(&model.App{})
@ -29,6 +23,18 @@ func (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, err
return count, apps, err
}
func (a AppRepo) GetFirst(opts ...DBOption) (model.App, error) {
var app model.App
db := global.DB.Model(&model.App{})
for _, opt := range opts {
db = opt(db)
}
if err := db.First(&app).Error; err != nil {
return app, err
}
return app, nil
}
func (a AppRepo) BatchCreate(ctx context.Context, apps []*model.App) error {
db := ctx.Value("db").(*gorm.DB)
return db.Omit(clause.Associations).Create(apps).Error

View File

@ -3,12 +3,34 @@ package repo
import (
"context"
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/global"
"gorm.io/gorm"
)
type AppDetailRepo struct {
}
func (a AppDetailRepo) WithVersion(version string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("version = ?", version)
}
}
func (a AppDetailRepo) WithAppId(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("app_id = ?", id)
}
}
func (a AppDetailRepo) GetAppDetail(opts ...DBOption) (model.AppDetail, error) {
var detail model.AppDetail
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&detail).Error
return detail, err
}
func (a AppDetailRepo) BatchCreate(ctx context.Context, details []*model.AppDetail) error {
db := ctx.Value("db").(*gorm.DB)
return db.Model(&model.AppDetail{}).Create(&details).Error
@ -19,7 +41,7 @@ func (a AppDetailRepo) DeleteByAppIds(ctx context.Context, appIds []uint) error
return db.Where("app_id in (?)", appIds).Delete(&model.AppDetail{}).Error
}
func (a AppDetailRepo) GetByAppId(ctx context.Context, appId string) ([]model.AppDetail, error) {
func (a AppDetailRepo) GetByAppId(ctx context.Context, appId uint) ([]model.AppDetail, error) {
db := ctx.Value("db").(*gorm.DB)
var details []model.AppDetail
if err := db.Where("app_id = ?", appId).Find(&details).Error; err != nil {

View File

@ -27,3 +27,11 @@ func (a AppTagRepo) GetByAppId(appId uint) ([]model.AppTag, error) {
}
return appTags, nil
}
func (a AppTagRepo) GetByTagIds(tagIds []uint) ([]model.AppTag, error) {
var appTags []model.AppTag
if err := global.DB.Where("tag_id in (?)", tagIds).Find(&appTags).Error; err != nil {
return nil, err
}
return appTags, nil
}

View File

@ -35,3 +35,11 @@ func (t TagRepo) GetByIds(ids []uint) ([]model.Tag, error) {
}
return tags, nil
}
func (t TagRepo) GetByKeys(keys []string) ([]model.Tag, error) {
var tags []model.Tag
if err := global.DB.Where("key in (?)", keys).Find(&tags).Error; err != nil {
return nil, err
}
return tags, nil
}

View File

@ -7,10 +7,12 @@ import (
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/app/repo"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/common"
"golang.org/x/net/context"
"os"
"path"
"reflect"
"sort"
)
type AppService struct {
@ -23,8 +25,25 @@ func (a AppService) Page(req dto.AppRequest) (interface{}, error) {
if req.Name != "" {
opts = append(opts, commonRepo.WithLikeName(req.Name))
}
if len(req.Types) != 0 {
opts = append(opts, appRepo.WithInTypes(req.Types))
if len(req.Tags) != 0 {
tags, err := tagRepo.GetByKeys(req.Tags)
if err != nil {
return nil, err
}
var tagIds []uint
for _, t := range tags {
tagIds = append(tagIds, t.ID)
}
appTags, err := appTagRepo.GetByTagIds(tagIds)
if err != nil {
return nil, err
}
var appIds []uint
for _, t := range appTags {
appIds = append(appIds, t.AppId)
}
opts = append(opts, commonRepo.WithIdsIn(appIds))
}
var res dto.AppRes
total, apps, err := appRepo.Page(req.Page, req.PageSize, opts...)
@ -62,6 +81,47 @@ func (a AppService) Page(req dto.AppRequest) (interface{}, error) {
return res, nil
}
func (a AppService) GetApp(id uint) (dto.AppDTO, error) {
var appDTO dto.AppDTO
app, err := appRepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return appDTO, err
}
appDTO.App = app
details, err := appDetailRepo.GetByAppId(context.WithValue(context.Background(), "db", global.DB), id)
if err != nil {
return appDTO, err
}
var versionsRaw []string
for _, detail := range details {
versionsRaw = append(versionsRaw, detail.Version)
}
sort.Slice(versionsRaw, func(i, j int) bool {
return common.CompareVersion(versionsRaw[i], versionsRaw[j])
})
appDTO.Versions = versionsRaw
return appDTO, nil
}
func (a AppService) GetAppDetail(appId uint, version string) (dto.AppDetailDTO, error) {
var (
appDetailDTO dto.AppDetailDTO
)
var opts []repo.DBOption
opts = append(opts, appDetailRepo.WithAppId(appId), appDetailRepo.WithVersion(version))
detail, err := appDetailRepo.GetAppDetail(opts...)
if err != nil {
return appDetailDTO, err
}
appDetailDTO.AppDetail = detail
return appDetailDTO, nil
}
func (a AppService) Sync() error {
//TODO 从 oss 拉取最新列表
var appConfig model.AppConfig

View File

@ -76,6 +76,7 @@ require (
github.com/google/flatbuffers v1.12.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect

View File

@ -247,6 +247,8 @@ github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
github.com/gwatts/gin-adapter v1.0.0 h1:TsmmhYTR79/RMTsfYJ2IQvI1F5KZ3ZFJxuQSYEOpyIA=
github.com/gwatts/gin-adapter v1.0.0/go.mod h1:44AEV+938HsS0mjfXtBDCUZS9vONlF2gwvh8wu4sRYc=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=

View File

@ -17,5 +17,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
{
appRouter.POST("/sync", baseApi.AppSync)
appRouter.POST("/search", baseApi.AppSearch)
appRouter.GET("/:id", baseApi.GetApp)
appRouter.GET("/detail/:appid/:version", baseApi.GetAppDetail)
}
}

View File

@ -0,0 +1,40 @@
package common
import (
"regexp"
"strconv"
"strings"
)
func CompareVersion(version1 string, version2 string) bool {
version1s := strings.Split(version1, ".")
version2s := strings.Split(version2, ".")
n := min(len(version1s), len(version2s))
re := regexp.MustCompile("[0-9]+")
for i := 0; i < n; i++ {
sVersion1s := re.FindAllString(version1s[i], -1)
sVersion2s := re.FindAllString(version2s[i], -1)
if len(sVersion1s) == 0 {
return false
}
if len(sVersion2s) == 0 {
return false
}
v1num, _ := strconv.Atoi(sVersion1s[0])
v2num, _ := strconv.Atoi(sVersion2s[0])
if v1num == v2num {
continue
} else {
return v1num > v2num
}
}
return true
}
func min(a, b int) int {
if a < b {
return a
}
return b
}

View File

@ -1,7 +1,7 @@
import { ReqPage } from '.';
import { ReqPage, CommonModel } from '.';
export namespace App {
export interface App {
export interface App extends CommonModel {
name: string;
icon: string;
key: string;
@ -12,6 +12,10 @@ export namespace App {
type: string;
}
export interface AppDTO extends App {
versions: string[];
}
export interface Tag {
key: string;
name: string;
@ -25,18 +29,17 @@ export namespace App {
tags: App.Tag[];
}
export interface AppDetail {
name: string;
export interface AppDetail extends CommonModel {
appId: string;
icon: string;
description: string;
sourceLink: string;
versions: string[];
version: string;
readme: string;
athor: string;
formFields: string;
dockerCompose: string;
}
export interface AppReq extends ReqPage {
name: string;
types: string[];
tags: string[];
}
}

View File

@ -1,4 +1,3 @@
// export const GetAppList = ()
import http from '@/api';
import { App } from '../interface/app';
@ -9,3 +8,11 @@ export const SyncApp = () => {
export const SearchApp = (req: App.AppReq) => {
return http.post<App.AppResPage>('apps/search', req);
};
export const GetApp = (id: number) => {
return http.get<App.AppDTO>('apps/' + id);
};
export const GetAppDetail = (id: number, version: string) => {
return http.get<App.AppDetail>('apps/detail/' + id + '/' + version);
};

View File

@ -392,5 +392,7 @@ export default {
installed: '已安装',
all: '全部',
version: '版本',
detail: '详情',
install: '安装',
},
};

View File

@ -16,17 +16,8 @@ const appStoreRouter = {
component: () => import('@/views/app-store/index.vue'),
meta: {},
},
// {
// path: '/apps/detail/:name',
// name: 'AppDetail',
// component: () => import('@/views/app-store/detail/index.vue'),
// meta: {
// hidden: true,
// title: 'menu.apps',
// },
// },
{
path: '/apps/detail/:name',
path: '/apps/detail/:id',
name: 'AppDetail',
props: true,
hidden: true,

View File

@ -1,29 +0,0 @@
{
"data": [
{
"name": "Mysql",
"icon": "mysql.png",
"description": "常用的关系型数据库",
"tags": ["数据库"]
},
{
"name": "Redis",
"icon": "redis.png",
"description": "缓存数据库",
"tags": ["数据库"]
},
{
"name": "Wordpress",
"icon": "wordpress.png",
"description": "老牌博客平台",
"tags": ["网站"]
},
{
"name": "Halo",
"icon": "halo.png",
"description": "现代化的博客平台",
"tags": ["网站"]
}
],
"tags": ["数据库", "网站", "测试", "开发"]
}

View File

@ -1,10 +0,0 @@
{
"name": "Halo",
"description": "更好用的博客模版",
"versions": ["0.0.1", "0.0.2"],
"sourceLink": "",
"athor": "halo",
"status": "",
"readme": "",
"icon": "halo.png"
}

View File

@ -1,138 +1,96 @@
<template>
<LayoutContent>
<LayoutContent :header="$t('app.detail')" :back-name="'App'">
<div class="brief">
<el-row :gutter="20">
<el-col :span="4">
<div class="icon">
<el-image class="image" :src="getImageUrl(appDetail.icon)"></el-image>
<el-image class="image" :src="'data:image/png;base64,' + app.icon"></el-image>
</div>
</el-col>
<el-col :span="20">
<div class="a-detail">
<div class="a-name">
<font size="5" style="font-weight: 800">{{ appDetail.name }}</font>
<font size="5" style="font-weight: 800">{{ app.name }}</font>
</div>
<div class="a-description">
<span>
<font>
{{ appDetail.description }}
{{ app.shortDesc }}
</font>
</span>
</div>
<br />
<el-descriptions :column="1">
<el-descriptions-item :label="$t('app.version')">
<el-select v-model="appSelect.version">
<el-option
v-for="(v, index) in appDetail.versions"
:key="index"
:value="v"
:label="v"
>
<el-select v-model="version" @change="getDetail(version)">
<el-option v-for="(v, index) in app.versions" :key="index" :value="v" :label="v">
{{ v }}
</el-option>
</el-select>
</el-descriptions-item>
<el-descriptions-item :label="'链接'">
<el-link>source</el-link>
<el-descriptions-item :label="'来源'">
<el-link @click="toLink(app.source)">
<el-icon><Link /></el-icon>
</el-link>
</el-descriptions-item>
<el-descriptions-item :label="'来源'">FIT2CLOUD</el-descriptions-item>
<el-descriptions-item :label="'作者'">{{ app.author }}</el-descriptions-item>
</el-descriptions>
<div>
<el-button type="primary">安装</el-button>
<el-button type="primary">{{ $t('app.install') }}</el-button>
</div>
</div>
</el-col>
</el-row>
</div>
<el-divider border-style="double" />
<div class="detail">
<v-md-preview :text="readme"></v-md-preview>
<div class="detail" v-loading="loadingDetail">
<v-md-preview :text="appDetail.readme"></v-md-preview>
</div>
</LayoutContent>
</template>
<script lang="ts" setup>
import { GetApp, GetAppDetail } from '@/api/modules/app';
import LayoutContent from '@/layout/layout-content.vue';
import { getAssetsFile } from '@/utils/image';
import { reactive, ref } from 'vue';
import detail from './detail.json';
import { onMounted, ref } from 'vue';
let appDetail = ref<any>();
appDetail.value = detail;
let appSelect = reactive({
version: '',
interface OperateProps {
id: number;
}
const props = withDefaults(defineProps<OperateProps>(), {
id: 0,
});
appSelect.version = appDetail.value.versions[0];
let app = ref<any>({});
let appDetail = ref<any>({});
let version = ref('');
let loadingDetail = ref(false);
let readme = ref<string>(`<p align="center">
<a href="https://halo.run" target="_blank" rel="noopener noreferrer">
<img width="100" src="https://halo.run/logo" alt="Halo logo" />
</a>
</p>
<p align="center"><b>Halo</b> [ˈheɪloʊ]一款现代化的开源博客/CMS系统值得一试</p>
<p align="center">
<a href="https://github.com/halo-dev/halo/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/halo-dev/halo.svg?style=flat-square" /></a>
<a href="https://github.com/halo-dev/halo/releases"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/halo-dev/halo/total.svg?style=flat-square" /></a>
<a href="https://hub.docker.com/r/halohub/halo"><img alt="Docker pulls" src="https://img.shields.io/docker/pulls/halohub/halo?style=flat-square" /></a>
<a href="https://github.com/halo-dev/halo/commits"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/halo-dev/halo.svg?style=flat-square" /></a>
<a href="https://github.com/halo-dev/halo/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/workflow/status/halo-dev/halo/Halo%20CI?style=flat-square" /></a>
<br />
<a href="https://halo.run">官网</a>
<a href="https://docs.halo.run">文档</a>
<a href="https://bbs.halo.run">社区</a>
<a href="https://gitee.com/halo-dev">Gitee</a>
<a href="https://t.me/halo_dev">Telegram 频道</a>
</p>
---
## 快速开始
详细部署文档请查阅<https://docs.halo.run>
## 在线体验
- 环境地址<https://demo.halo.run>
- 后台地址<https://demo.halo.run/admin>
- 用户名demo
- 密码P@ssw0rd123..
- 使用前请阅读<https://demo.halo.run/archives/tips>
## 生态
| 项目 | 状态 | 描述 |
| --- | --- | --- |
| [halo-admin](https://github.com/halo-dev/halo-admin) | <a href="https://github.com/halo-dev/halo-admin/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/halo-dev/halo-admin.svg?style=flat-square" /></a> | Web UI |
| [js-sdk](https://github.com/halo-dev/js-sdk) | <a href="https://github.com/halo-dev/js-sdk"><img alt="npm release" src="https://img.shields.io/npm/v/@halo-dev/content-api?style=flat-square"/></a> | JavaScript SDK |
| [halo-comment](https://github.com/halo-dev/halo-comment) | <a href="https://www.npmjs.com/package/halo-comment"><img alt="npm release" src="https://img.shields.io/npm/v/halo-comment?style=flat-square"/></a> | 便 |
| [halo-comment-normal](https://github.com/halo-dev/halo-comment-normal) | <a href="https://www.npmjs.com/package/halo-comment-normal"><img alt="npm release" src="https://img.shields.io/npm/v/halo-comment-normal?style=flat-square"/></a> | |
| [halo-mobile-app](https://github.com/halo-dev/halo-mobile-app) | | APP |
| [tencent-cloudbase-halo](https://github.com/halo-dev/tencent-cloudbase-halo) | | CloudBase |
| [halo-theme-\*](https://github.com/topics/halo-theme) | | GitHub Halo |
## 许可证
[![license](https://img.shields.io/github/license/halo-dev/halo.svg?style=flat-square)](https://github.com/halo-dev/halo/blob/master/LICENSE)
Halo 使用 GPL-v3.0 协议开源请遵守开源协议
## 贡献
参考 [CONTRIBUTING](https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md)
<a href="https://github.com/halo-dev/halo/graphs/contributors"><img src="https://opencollective.com/halo/contributors.svg?width=890&button=false" /></a>
## 状态
![Repobeats analytics](https://repobeats.axiom.co/api/embed/ad008b2151c22e7cf734d2688befaa795d593b95.svg 'Repobeats analytics image')
`);
const getImageUrl = (name: string) => {
return getAssetsFile(name);
const getApp = () => {
GetApp(props.id).then((res) => {
app.value = res.data;
version.value = app.value.versions[0];
getDetail(version.value);
});
};
const getDetail = (version: string) => {
loadingDetail.value = true;
GetAppDetail(props.id, version)
.then((res) => {
appDetail.value = res.data;
})
.finally(() => {
loadingDetail.value = false;
});
};
const toLink = (link: string) => {
window.open(link, '_blank');
};
onMounted(() => {
getApp();
});
</script>
<style lang="scss">

View File

@ -1,31 +1,19 @@
<template>
<LayoutContent>
<el-row :gutter="20">
<!-- <el-col :span="24">
<div class="header">
<el-radio-group v-model="activeName">
<el-radio-button label="all">
{{ $t('app.all') }}
</el-radio-button>
<el-radio-button label="installed">
{{ $t('app.installed') }}
</el-radio-button>
</el-radio-group>
</div>
</el-col> -->
<el-col :span="12">
<el-input></el-input>
<el-input v-model="req.name" @blur="searchByName"></el-input>
</el-col>
<el-col :span="12">
<el-select v-model="selectTags" multiple style="width: 100%">
<el-select v-model="req.tags" multiple style="width: 100%" @change="changeTag">
<el-option v-for="item in tags" :key="item.key" :label="item.name" :value="item.key"></el-option>
</el-select>
</el-col>
<el-button @click="sync">同步</el-button>
<!-- <el-button @click="sync">同步</el-button> -->
</el-row>
<el-row :gutter="20">
<el-col v-for="(app, index) in apps" :key="index" :xs="8" :sm="8" :lg="4">
<div @click="getAppDetail(app.name)">
<div @click="getAppDetail(app.id)">
<el-card :body-style="{ padding: '0px' }" class="a-card">
<el-row :gutter="24">
<el-col :span="8">
@ -63,32 +51,31 @@
<script lang="ts" setup>
import { App } from '@/api/interface/app';
import LayoutContent from '@/layout/layout-content.vue';
import { onMounted, ref } from 'vue';
import { onMounted, reactive, ref } from 'vue';
import router from '@/routers';
import { SyncApp } from '@/api/modules/app';
// import { SyncApp } from '@/api/modules/app';
import { SearchApp } from '@/api/modules/app';
let req = ref<App.AppReq>({
let req = reactive({
name: '',
types: [],
tags: [],
page: 1,
pageSize: 50,
});
let apps = ref<App.App[]>([]);
let tags = ref<App.Tag[]>([]);
let selectTags = ref<string[]>([]);
const colorArr = ['#6495ED', '#54FF9F', '#BEBEBE', '#FFF68F', '#FFFF00', '#8B0000'];
const getColor = (index: number) => {
return colorArr[index];
};
const sync = () => {
SyncApp().then((res) => {
console.log(res);
});
};
// const sync = () => {
// SyncApp().then((res) => {
// console.log(res);
// });
// };
const search = async (req: App.AppReq) => {
await SearchApp(req).then((res) => {
@ -97,15 +84,24 @@ const search = async (req: App.AppReq) => {
});
};
const getAppDetail = (name: string) => {
const getAppDetail = (id: number) => {
console.log(id);
let params: { [key: string]: any } = {
name: name,
id: id,
};
router.push({ name: 'AppDetail', params });
};
const changeTag = () => {
search(req);
};
const searchByName = () => {
search(req);
};
onMounted(() => {
search(req.value);
search(req);
});
</script>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB