1202 lines
24 KiB
Go
1202 lines
24 KiB
Go
package setup
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/yaoapp/gou"
|
|
"github.com/yaoapp/gou/fs"
|
|
"github.com/yaoapp/gou/fs/dsl"
|
|
"github.com/yaoapp/gou/fs/system"
|
|
"github.com/yaoapp/kun/log"
|
|
"github.com/yaoapp/yao/engine"
|
|
"github.com/yaoapp/yao/share"
|
|
"github.com/yaoapp/yao/widgets/app"
|
|
)
|
|
|
|
// Install app install
|
|
//
|
|
// {
|
|
// "env": {
|
|
// "YAO_LANG": "中文",
|
|
// "YAO_ENV": "开发模式(推荐)",
|
|
// "YAO_PORT": "5099",
|
|
// "YAO_STUDIO_PORT": "5077"
|
|
// },
|
|
// "db": {
|
|
// "type": "sqlite",
|
|
// "option.file": "db/yao.db"
|
|
// }
|
|
// }
|
|
//
|
|
// {
|
|
// "env": {
|
|
// "YAO_LANG": "中文",
|
|
// "YAO_ENV": "开发模式(推荐)",
|
|
// "YAO_PORT": "5099",
|
|
// "YAO_STUDIO_PORT": "5077"
|
|
// },
|
|
// "db": {
|
|
// "type": "mysql",
|
|
// "option.db": "yao",
|
|
// "option.host.host": "127.0.0.1",
|
|
// "option.host.port": "3306",
|
|
// "option.host.user": "root",
|
|
// "option.host.pass": "123456"
|
|
// }
|
|
// }
|
|
func Install(payload map[string]map[string]string) error {
|
|
|
|
dbOption, err := getDBOption(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = ValidateDB(dbOption)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
envOption, err := getENVOption(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = ValidateHosting(envOption)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
root := appRoot()
|
|
err = makeService(root, "0.0.0.0", envOption["YAO_PORT"], envOption["YAO_STUDIO_PORT"], envOption["YAO_LANG"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = makeDB(root, dbOption)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = makeSession(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = makeLog(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = makeDirs(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = makeDemoWidgets(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = makeMigrate(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = makeSetup(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeService(root string, host string, port string, studioPort string, lang string) error {
|
|
file := filepath.Join(root, ".env")
|
|
err := envSet(file, "YAO_HOST", host)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = envSet(file, "YAO_PORT", port)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = envSet(file, "YAO_STUDIO_PORT", studioPort)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return envSet(file, "YAO_LANG", lang)
|
|
}
|
|
|
|
func makeDB(root string, option map[string]string) error {
|
|
|
|
driver, dsn, err := getDSN(option)
|
|
if driver != "mysql" && driver != "sqlite3" {
|
|
return fmt.Errorf("数据库驱动应该为: mysql/sqlite3")
|
|
}
|
|
|
|
file := filepath.Join(root, ".env")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = envSet(file, "YAO_DB_DRIVER", driver)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return envSet(file, "YAO_DB_PRIMARY", dsn)
|
|
}
|
|
|
|
func makeSession(root string) error {
|
|
file := filepath.Join(root, ".env")
|
|
if has, _ := envHas(file, "YAO_SESSION_STORE"); has {
|
|
return nil
|
|
}
|
|
|
|
if has, _ := envHas(file, "YAO_SESSION_FILE"); has {
|
|
return nil
|
|
}
|
|
|
|
err := os.MkdirAll(filepath.Join(root, "data"), os.ModePerm)
|
|
if err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
|
|
err = envSet(file, "YAO_SESSION_STORE", "file")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ssfile := filepath.Join(root, "db", ".session")
|
|
return envSet(file, "YAO_SESSION_FILE", ssfile)
|
|
}
|
|
|
|
func makeLog(root string) error {
|
|
file := filepath.Join(root, ".env")
|
|
if has, _ := envHas(file, "YAO_LOG_MODE"); has {
|
|
return nil
|
|
}
|
|
|
|
if has, _ := envHas(file, "YAO_LOG"); has {
|
|
return nil
|
|
}
|
|
|
|
err := os.MkdirAll(filepath.Join(root, "data"), os.ModePerm)
|
|
if err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
|
|
err = envSet(file, "YAO_LOG_MODE", "TEXT")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logfile := filepath.Join(root, "logs", "application.log")
|
|
return envSet(file, "YAO_LOG", logfile)
|
|
}
|
|
|
|
func makeDirs(root string) error {
|
|
|
|
err := os.MkdirAll(filepath.Join(root, "public"), os.ModePerm)
|
|
if err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(root, "services"), os.ModePerm)
|
|
if err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(root, "studio"), os.ModePerm)
|
|
if err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(root, "logs"), os.ModePerm)
|
|
if err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(root, "db"), os.ModePerm)
|
|
if err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeDemoWidgets(root string) error {
|
|
|
|
dsl := dsl.New(root)
|
|
|
|
// make app
|
|
ok, err := makeAppWidget(dsl, root)
|
|
if err != nil || !ok {
|
|
return err
|
|
}
|
|
|
|
// make flows
|
|
err = makeMenuFlowWidget(dsl, root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// make logins
|
|
err = makeLoginWidget(dsl, root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// make models
|
|
err = makeModelWidget(dsl, root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// make tables & forms
|
|
err = makeTableFormWidget(dsl, root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// make chart
|
|
err = makeChartWidget(dsl, root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// make scripts
|
|
err = makeScript(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// make scripts
|
|
err = makeLang(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// make index page
|
|
err = makeIndexPage(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeAppWidget(dsl fs.FileSystem, root string) (bool, error) {
|
|
|
|
file := filepath.Join("app.json")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return false, nil
|
|
}
|
|
|
|
_, err := dsl.WriteFile(file, []byte(fmt.Sprintf(`
|
|
{
|
|
"xgen": "1.0",
|
|
"name": "::Demo Application",
|
|
"short": "::Demo",
|
|
"description": "::Another yao application",
|
|
"version": "%s",
|
|
"adminRoot": "admin",
|
|
"setup": "scripts.demo.Data",
|
|
"menu": {
|
|
"process": "flows.app.menu",
|
|
"args": ["demo"]
|
|
},
|
|
"optional": {
|
|
"hideNotification": true,
|
|
"hideSetting": false
|
|
}
|
|
}
|
|
`, share.VERSION)), 0644)
|
|
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func makeMenuFlowWidget(dsl fs.FileSystem, root string) error {
|
|
|
|
file := filepath.Join("flows", "app", "menu.flow.json")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
|
|
_, err := dsl.WriteFile(file, []byte(`
|
|
{
|
|
"name": "APP Menu",
|
|
"nodes": [],
|
|
"output": [
|
|
{
|
|
"blocks": 0,
|
|
"icon": "icon-activity",
|
|
"id": 1,
|
|
"name": "图表",
|
|
"parent": null,
|
|
"path": "/x/Chart/dashboard",
|
|
"visible_menu": 0
|
|
},
|
|
{
|
|
"blocks": 0,
|
|
"icon": "icon-book",
|
|
"id": 2,
|
|
"name": "表格",
|
|
"parent": null,
|
|
"path": "/x/Table/pet",
|
|
"visible_menu": 1,
|
|
"children": [
|
|
{
|
|
"blocks": 0,
|
|
"icon": "icon-book",
|
|
"name": "宠物列表",
|
|
"id": 2010,
|
|
"parent": 2,
|
|
"path": "/x/Table/pet",
|
|
"visible_menu": 1
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"blocks": 0,
|
|
"icon": "icon-clipboard",
|
|
"id": 2,
|
|
"name": "表单",
|
|
"parent": null,
|
|
"path": "/x/Form/pet/1/edit",
|
|
"visible_menu": 1,
|
|
"children": [
|
|
{
|
|
"blocks": 0,
|
|
"icon": "icon-clipboard",
|
|
"name": "编辑模式",
|
|
"id": 2010,
|
|
"parent": 2,
|
|
"path": "/x/Form/pet/1/edit",
|
|
"visible_menu": 1
|
|
},
|
|
{
|
|
"blocks": 0,
|
|
"icon": "icon-clipboard",
|
|
"name": "查看模式",
|
|
"id": 2010,
|
|
"parent": 2,
|
|
"path": "/x/Form/pet/1/view",
|
|
"visible_menu": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeLoginWidget(dsl fs.FileSystem, root string) error {
|
|
file := filepath.Join("logins", "admin.login.json")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
_, err := dsl.WriteFile(file, []byte(`
|
|
{
|
|
"name": "::Admin Login",
|
|
"action": {
|
|
"process": "yao.login.Admin",
|
|
"args": [":payload"]
|
|
},
|
|
"layout": {
|
|
"entry": "/x/Chart/dashboard",
|
|
"slogan": "::Make Your Dream With Yao App Engine",
|
|
"site": "https://yaoapps.com?from=instance-admin-login"
|
|
}
|
|
}
|
|
`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
file = filepath.Join("logins", "user.login.json")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
_, err = dsl.WriteFile(file, []byte(`
|
|
{
|
|
"name": "::User Login",
|
|
"action": {
|
|
"process": "scripts.user.Login",
|
|
"args": [":payload"]
|
|
},
|
|
"layout": {
|
|
"entry": "/x/Table/pet",
|
|
"slogan": "::Make Your Dream With Yao App Engine",
|
|
"site": "https://yaoapps.com/from=instance-user-login"
|
|
}
|
|
}
|
|
`), 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeModelWidget(dsl fs.FileSystem, root string) error {
|
|
file := filepath.Join("models", "pet.mod.json")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
_, err := dsl.WriteFile(file, []byte(`
|
|
{
|
|
"name": "Pet",
|
|
"table": { "name": "pet", "comment": "宠物表" },
|
|
"columns": [
|
|
{ "name": "id", "comment": "ID", "type": "ID" },
|
|
{
|
|
"name": "name",
|
|
"comment": "昵称",
|
|
"type": "string",
|
|
"length": 80,
|
|
"index": true,
|
|
"nullable": true
|
|
},
|
|
{
|
|
"name": "type",
|
|
"comment": "类型",
|
|
"type": "enum",
|
|
"option": ["cat", "dog", "others"],
|
|
"index": true
|
|
},
|
|
{
|
|
"name": "status",
|
|
"comment": "入院状态",
|
|
"type": "enum",
|
|
"option": ["checked", "curing", "cured"],
|
|
"index": true
|
|
},
|
|
{
|
|
"name": "mode",
|
|
"comment": "状态",
|
|
"type": "enum",
|
|
"option": ["enabled", "disabled"],
|
|
"index": true
|
|
},
|
|
{
|
|
"name": "online",
|
|
"comment": "是否在线",
|
|
"type": "boolean",
|
|
"default": false,
|
|
"index": true
|
|
},
|
|
{
|
|
"name": "curing_status",
|
|
"comment": "治疗状态",
|
|
"type": "enum",
|
|
"default": "0",
|
|
"option": ["0", "1", "2"],
|
|
"index": true
|
|
},
|
|
{
|
|
"name": "stay",
|
|
"comment": "入院时间",
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"name": "cost",
|
|
"comment": "花费",
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"name": "doctor_id",
|
|
"type": "integer",
|
|
"comment": "相关职工",
|
|
"nullable": true
|
|
},
|
|
{
|
|
"name": "images",
|
|
"type": "json",
|
|
"comment": "相关图片",
|
|
"nullable": true
|
|
},
|
|
{
|
|
"name": "test_string",
|
|
"comment": "测试字段(字符串)",
|
|
"type": "string",
|
|
"nullable": true
|
|
},
|
|
{
|
|
"name": "test_number",
|
|
"comment": "测试字段(数字)",
|
|
"type": "integer",
|
|
"nullable": true
|
|
},
|
|
{
|
|
"name": "test_array",
|
|
"comment": "测试字段(数组)",
|
|
"type": "json",
|
|
"nullable": true
|
|
}
|
|
],
|
|
"relations": {},
|
|
"values": [],
|
|
"indexes": [],
|
|
"option": { "timestamps": true,"soft_deletes": true }
|
|
}
|
|
`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeTableFormWidget(dsl fs.FileSystem, root string) error {
|
|
file := filepath.Join("tables", "pet.tab.json")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
_, err := dsl.WriteFile(file, []byte(`
|
|
{
|
|
"name": "::Pet Admin Bind Model",
|
|
"action": {
|
|
"bind": { "model": "pet", "option": {} }
|
|
}
|
|
}
|
|
`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
file = filepath.Join("forms", "pet.form.json")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
_, err = dsl.WriteFile(file, []byte(`
|
|
{
|
|
"name": "::Pet Admin Bind Table",
|
|
"action": {
|
|
"bind": { "table": "pet", "option": {} }
|
|
}
|
|
}
|
|
`), 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeChartWidget(dsl fs.FileSystem, root string) error {
|
|
|
|
file := filepath.Join("charts", "dashboard.chart.json")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
|
|
_, err := dsl.WriteFile(file, []byte(`
|
|
{
|
|
"name": "宠物医院数据图表",
|
|
|
|
"config":{"full":false},
|
|
|
|
"action": {
|
|
"before:data": "scripts.stat.BeforeData",
|
|
"data": { "process": "scripts.stat.Data", "default": ["2022-09-20"] },
|
|
"after:data": "scripts.stat.AfterData"
|
|
},
|
|
|
|
"layout": {
|
|
"operation": {
|
|
"actions": [
|
|
{
|
|
"title": "跳转至大屏",
|
|
"icon": "icon-airplay",
|
|
"action": { "Common.historyPush": { "pathname": "/x/Cool/demo" } }
|
|
}
|
|
]
|
|
},
|
|
|
|
"filter": {
|
|
"columns": [{ "name": "时间区间", "width": 6 }]
|
|
},
|
|
|
|
"chart": {
|
|
"columns": [
|
|
{ "name": "宠物数量", "width": 6 },
|
|
{ "name": "宠物类型", "width": 6 },
|
|
{ "name": "当月收入", "width": 6 },
|
|
{ "name": "医师数量", "width": 6 },
|
|
{ "name": "宠物数量_上月", "width": 6 },
|
|
{ "name": "宠物类型_上月", "width": 6 },
|
|
{ "name": "当月收入_上月", "width": 6 },
|
|
{ "name": "医师数量_上月", "width": 6 },
|
|
{ "name": "收入", "width": 8 },
|
|
{ "name": "支出", "width": 8 },
|
|
{ "name": "综合评分", "width": 8 },
|
|
{ "name": "收入_折线图", "width": 8 },
|
|
{ "name": "支出_折线图", "width": 8 },
|
|
{ "name": "综合评分_折线图", "width": 8 },
|
|
{ "name": "类型排布", "width": 12 },
|
|
{ "name": "状态分布", "width": 12 },
|
|
{ "name": "综合消费", "width": 24 }
|
|
]
|
|
}
|
|
},
|
|
|
|
"fields": {
|
|
"filter": {
|
|
"时间区间": {
|
|
"bind": "range",
|
|
"edit": { "type": "RangePicker", "props": {} }
|
|
},
|
|
"状态": {
|
|
"bind": "status",
|
|
"edit": {
|
|
"type": "Select",
|
|
"props": {
|
|
"xProps": {
|
|
"$remote": {
|
|
"process": "models.pet.Get",
|
|
"query": { "select": "name,status", "limit": 2 }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"chart": {
|
|
"收入": {
|
|
"bind": "income",
|
|
"link": "/x/Table/pet",
|
|
"out": "scripts.stat.Income",
|
|
"view": {
|
|
"type": "NumberChart",
|
|
"props": {
|
|
"chartHeight": 150,
|
|
"prefix": "¥",
|
|
"decimals": 2,
|
|
"nameKey": "date",
|
|
"valueKey": "value"
|
|
}
|
|
}
|
|
},
|
|
"支出": {
|
|
"bind": "cost",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "NumberChart",
|
|
"props": {
|
|
"chartHeight": 150,
|
|
"color": "red",
|
|
"prefix": "¥",
|
|
"decimals": 2,
|
|
"nameKey": "date",
|
|
"valueKey": "value"
|
|
}
|
|
}
|
|
},
|
|
"综合评分": {
|
|
"bind": "rate",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "NumberChart",
|
|
"props": {
|
|
"chartHeight": 150,
|
|
"color": "orange",
|
|
"unit": "分",
|
|
"decimals": 1,
|
|
"nameKey": "date",
|
|
"valueKey": "value"
|
|
}
|
|
}
|
|
},
|
|
"收入_折线图": {
|
|
"bind": "income",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "NumberChart",
|
|
"props": {
|
|
"cardStyle": { "padding": 0 },
|
|
"type": "line",
|
|
"chartHeight": 120,
|
|
"prefix": "¥",
|
|
"decimals": 2,
|
|
"nameKey": "date",
|
|
"valueKey": "value"
|
|
}
|
|
}
|
|
},
|
|
"支出_折线图": {
|
|
"bind": "cost",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "NumberChart",
|
|
"props": {
|
|
"cardStyle": { "padding": 0 },
|
|
"type": "line",
|
|
"chartHeight": 120,
|
|
"color": "red",
|
|
"prefix": "¥",
|
|
"decimals": 2,
|
|
"nameKey": "date",
|
|
"valueKey": "value"
|
|
}
|
|
}
|
|
},
|
|
"综合评分_折线图": {
|
|
"bind": "rate",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "NumberChart",
|
|
"props": {
|
|
"cardStyle": { "padding": 0 },
|
|
"type": "line",
|
|
"chartHeight": 120,
|
|
"color": "orange",
|
|
"unit": "分",
|
|
"decimals": 1,
|
|
"nameKey": "date",
|
|
"valueKey": "value"
|
|
}
|
|
}
|
|
},
|
|
"宠物数量": {
|
|
"bind": "pet_count",
|
|
"link": "/x/Table/pet",
|
|
"view": { "type": "Number", "props": { "unit": "个" } }
|
|
},
|
|
"宠物类型": {
|
|
"bind": "pet_type",
|
|
"view": { "type": "Number", "props": { "unit": "种" } }
|
|
},
|
|
"当月收入": {
|
|
"bind": "income_monthly",
|
|
"view": { "type": "Number", "props": { "unit": "元" } }
|
|
},
|
|
"医师数量": {
|
|
"bind": "doctor_count",
|
|
"view": { "type": "Number", "props": { "unit": "个" } }
|
|
},
|
|
"宠物数量_上月": {
|
|
"bind": "prev_pet_count",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "Number",
|
|
"props": {
|
|
"cardStyle": { "padding": 0 },
|
|
"unit": "个",
|
|
"prev_title": "上月数据"
|
|
}
|
|
}
|
|
},
|
|
"宠物类型_上月": {
|
|
"bind": "prev_pet_type",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "Number",
|
|
"props": {
|
|
"cardStyle": { "padding": 0 },
|
|
"unit": "种",
|
|
"prev_title": "上月数据"
|
|
}
|
|
}
|
|
},
|
|
"当月收入_上月": {
|
|
"bind": "prev_income_monthly",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "Number",
|
|
"props": {
|
|
"cardStyle": { "padding": 0 },
|
|
"unit": "元",
|
|
"prev_title": "上月数据"
|
|
}
|
|
}
|
|
},
|
|
"医师数量_上月": {
|
|
"bind": "prev_doctor_count",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "Number",
|
|
"props": {
|
|
"cardStyle": { "padding": 0 },
|
|
"unit": "个",
|
|
"prev_title": "上月数据"
|
|
}
|
|
}
|
|
},
|
|
"类型排布": {
|
|
"bind": "datasource_type",
|
|
"link": "/x/Table/pet",
|
|
"view": {
|
|
"type": "Pie",
|
|
"props": {
|
|
"height": 240,
|
|
"nameKey": "type",
|
|
"series": [
|
|
{
|
|
"valueKey": "count",
|
|
"roseType": "area",
|
|
"radius": [10, 100],
|
|
"center": ["60%", "50%"],
|
|
"itemStyle": { "borderRadius": 6 }
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"refer": {
|
|
"type": "Table",
|
|
"props": {
|
|
"columns": [
|
|
{ "title": "类型", "dataIndex": "type" },
|
|
{ "title": "数量", "dataIndex": "count" }
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"状态分布": {
|
|
"bind": "datasource_status",
|
|
"view": {
|
|
"type": "Bar",
|
|
"props": {
|
|
"height": 240,
|
|
"nameKey": "status",
|
|
"axisLabel": { "interval": 0, "fontSize": 12 },
|
|
"series": [
|
|
{
|
|
"valueKey": "count",
|
|
"type": "bar",
|
|
"colorBy": "data",
|
|
"itemStyle": { "borderRadius": 6 },
|
|
"splitLine": { "show": false },
|
|
"axisLabel": { "show": false }
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"refer": {
|
|
"type": "Table",
|
|
"props": {
|
|
"columns": [
|
|
{ "title": "状态", "dataIndex": "status" },
|
|
{ "title": "数量", "dataIndex": "count" }
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"综合消费": {
|
|
"bind": "datasource_cost",
|
|
"view": {
|
|
"type": "LineBar",
|
|
"props": {
|
|
"height": 240,
|
|
"nameKey": "name",
|
|
"axisLabel": { "interval": 0, "fontSize": 12 },
|
|
"series": [
|
|
{
|
|
"valueKey": "stay",
|
|
"type": "line",
|
|
"smooth": true,
|
|
"symbolSize": 8,
|
|
"itemStyle": { "borderRadius": 6 },
|
|
"splitLine": { "show": false },
|
|
"axisLabel": { "show": false }
|
|
},
|
|
{
|
|
"valueKey": "cost",
|
|
"type": "bar",
|
|
"itemStyle": { "borderRadius": 6 },
|
|
"splitLine": { "show": false },
|
|
"axisLabel": { "show": false }
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeScript(root string) error {
|
|
|
|
fs := system.New(filepath.Join(root, "scripts"))
|
|
file := "demo.js"
|
|
if _, err := os.Stat(filepath.Join(root, "scripts", file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
_, err := fs.WriteFile(file, []byte(`
|
|
/**
|
|
* Demo Data
|
|
*/
|
|
function Data() {
|
|
return Process(
|
|
"yao.table.Insert",
|
|
"pet",
|
|
["name", "type", "status", "mode", "stay", "cost", "doctor_id"],
|
|
[
|
|
["Cookie", "cat", "checked", "enabled", 200, 105, 1],
|
|
["Baby", "dog", "checked", "enabled", 186, 24, 1],
|
|
["Poo", "others", "checked", "enabled", 199, 66, 1],
|
|
]
|
|
);
|
|
}
|
|
`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
file = filepath.Join("stat.js")
|
|
if _, err := os.Stat(filepath.Join(root, "scripts", file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
_, err = fs.WriteFile(file, []byte(`
|
|
/**
|
|
* before:data hook
|
|
* @param {*} params
|
|
* @returns
|
|
*/
|
|
function BeforeData(params) {
|
|
log.Info("[chart] before data hook: %s", JSON.stringify(params));
|
|
return [params];
|
|
}
|
|
|
|
/**
|
|
* after:data hook
|
|
* @param {*} data
|
|
* @returns
|
|
*/
|
|
function AfterData(data) {
|
|
log.Info("[chart] after data hook: %s", JSON.stringify(data));
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Get Data
|
|
* @param {*} params
|
|
*/
|
|
function Data(params) {
|
|
log.Info("[chart] process data query: %s", JSON.stringify(params));
|
|
return {
|
|
income: [
|
|
{ value: 40300, date: "2022-1-1" },
|
|
{ value: 50800, date: "2022-2-1" },
|
|
{ value: 31300, date: "2022-3-1" },
|
|
{ value: 48800, date: "2022-4-1" },
|
|
{ value: 69900, date: "2022-5-1" },
|
|
{ value: 37800, date: "2022-6-1" },
|
|
],
|
|
cost: [
|
|
{ value: 28100, date: "2022-1-1" },
|
|
{ value: 23000, date: "2022-2-1" },
|
|
{ value: 29300, date: "2022-3-1" },
|
|
{ value: 26700, date: "2022-4-1" },
|
|
{ value: 26400, date: "2022-5-1" },
|
|
{ value: 31200, date: "2022-6-1" },
|
|
],
|
|
rate: [
|
|
{ value: 8.0, date: "2022-1-1" },
|
|
{ value: 7.6, date: "2022-2-1" },
|
|
{ value: 9.1, date: "2022-3-1" },
|
|
{ value: 8.4, date: "2022-4-1" },
|
|
{ value: 6.9, date: "2022-5-1" },
|
|
{ value: 9.0, date: "2022-6-1" },
|
|
],
|
|
pet_count: 54,
|
|
pet_type: 8,
|
|
income_monthly: 68900,
|
|
doctor_count: 23,
|
|
prev_pet_count: { current: 54, prev: 45 },
|
|
prev_pet_type: { current: 8, prev: 13 },
|
|
prev_income_monthly: { current: 68900, prev: 92000 },
|
|
prev_doctor_count: { current: 23, prev: 27 },
|
|
datasource_type: [
|
|
{ type: "猫猫", count: 18 },
|
|
{ type: "狗狗", count: 6 },
|
|
{ type: "其他", count: 3 },
|
|
],
|
|
datasource_status: [
|
|
{ status: "已查看", count: 3 },
|
|
{ status: "治疗中", count: 12 },
|
|
{ status: "已治愈", count: 9 },
|
|
],
|
|
datasource_cost: [
|
|
{ name: "毛毛", stay: 3, cost: 2000 },
|
|
{ name: "阿布", stay: 6, cost: 4200 },
|
|
{ name: "咪咪", stay: 7, cost: 6000 },
|
|
{ name: "狗蛋", stay: 1, cost: 1000 },
|
|
],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Compute out
|
|
* @param {*} field
|
|
* @param {*} value
|
|
* @param {*} data
|
|
* @returns
|
|
*/
|
|
function Income(field, value, data) {
|
|
log.Info(
|
|
"[chart] Income Compute: %s",
|
|
JSON.stringify({ field: field, value: value, data: data })
|
|
);
|
|
return value;
|
|
}
|
|
`), 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeLang(root string) error {
|
|
fs := system.New(filepath.Join(root, "langs"))
|
|
file := filepath.Join("zh-cn", "global.yml")
|
|
if _, err := os.Stat(filepath.Join(root, "langs", file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
|
|
_, err := fs.WriteFile(file, []byte(`
|
|
Demo: 演示
|
|
Demo Application: 示例应用
|
|
Another yao application: 又一个 YAO 应用`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
file = filepath.Join("zh-hk", "langs", "global.yml")
|
|
if _, err := os.Stat(filepath.Join(root, file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
_, err = fs.WriteFile(file, []byte(`
|
|
Demo: 演示
|
|
Demo Application: 示例應用
|
|
Another yao application: 又一個YAO應用`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeIndexPage(root string) error {
|
|
fs := system.New(filepath.Join(root, "public"))
|
|
file := "index.html"
|
|
if _, err := os.Stat(filepath.Join(root, "public", file)); err == nil { // exists
|
|
return nil
|
|
}
|
|
|
|
_, err := fs.WriteFile(file, []byte(`
|
|
It works!`), 0644)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeMigrate(root string) error {
|
|
|
|
cfg, err := getConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = engine.Load(cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Do Stuff Here
|
|
for _, mod := range gou.Models {
|
|
has, err := mod.HasTable()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if has {
|
|
log.Warn("%s (%s) table already exists", mod.ID, mod.MetaData.Table.Name)
|
|
continue
|
|
}
|
|
|
|
err = mod.Migrate(false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeSetup(root string) error {
|
|
|
|
cfg, err := getConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = engine.Load(cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if app.Setting != nil && app.Setting.Setup != "" {
|
|
|
|
if strings.HasPrefix(app.Setting.Setup, "studio.") {
|
|
names := strings.Split(app.Setting.Setup, ".")
|
|
if len(names) < 3 {
|
|
return fmt.Errorf("setup studio script %s error", app.Setting.Setup)
|
|
}
|
|
|
|
service := strings.Join(names[1:len(names)-1], ".")
|
|
method := names[len(names)-1]
|
|
req := gou.Yao.New(service, method)
|
|
_, err := req.RootCall(cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
p, err := gou.ProcessOf(app.Setting.Setup, cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = p.Exec()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|