默认移除 iot、cloud、design 模块,将以其他方式按需引入
@ -26,7 +26,6 @@
|
||||
"mitt": "^3.0.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"monaco-editor": "0.36.0",
|
||||
"mqtt": "^4.3.7",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.7",
|
||||
"socket.io-client": "^4.7.2",
|
||||
|
@ -11,7 +11,10 @@ export default defineComponent({
|
||||
type: null,
|
||||
default: UserFilled
|
||||
},
|
||||
size: [String, Number] as PropType<"large" | "default" | "small" | number>,
|
||||
size: {
|
||||
type: [String, Number] as PropType<"large" | "default" | "small" | number>,
|
||||
default: 40
|
||||
},
|
||||
shape: {
|
||||
type: String as PropType<"circle" | "square">,
|
||||
default: "square"
|
||||
@ -24,12 +27,18 @@ export default defineComponent({
|
||||
|
||||
setup(props) {
|
||||
return () => {
|
||||
const height = props.size + "px";
|
||||
|
||||
return (
|
||||
<div
|
||||
class="cl-avatar"
|
||||
style={{
|
||||
height
|
||||
}}
|
||||
>
|
||||
<el-avatar
|
||||
style={{
|
||||
display: "block",
|
||||
margin: "auto",
|
||||
height: props.size + "px",
|
||||
height,
|
||||
width: props.size + "px"
|
||||
}}
|
||||
{...{
|
||||
@ -37,6 +46,7 @@ export default defineComponent({
|
||||
src: props.modelValue || props.src
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
<div
|
||||
class="cl-image"
|
||||
:style="{
|
||||
justifyContent: justify,
|
||||
height: style.h
|
||||
}"
|
||||
>
|
||||
@ -50,10 +49,6 @@ export default defineComponent({
|
||||
fit: {
|
||||
type: String as PropType<"" | "contain" | "cover" | "none" | "fill" | "scale-down">,
|
||||
default: "cover"
|
||||
},
|
||||
justify: {
|
||||
type: String,
|
||||
default: "center"
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,123 +0,0 @@
|
||||
<template>
|
||||
<cl-dialog v-model="visible" :title="title" width="1200px">
|
||||
<cl-crud ref="Crud" padding="0">
|
||||
<cl-row>
|
||||
<!-- 刷新按钮 -->
|
||||
<cl-refresh-btn />
|
||||
</cl-row>
|
||||
|
||||
<cl-row>
|
||||
<!-- 数据表格 -->
|
||||
<cl-table ref="Table" />
|
||||
</cl-row>
|
||||
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<!-- 分页控件 -->
|
||||
<cl-pagination />
|
||||
</cl-row>
|
||||
</cl-crud>
|
||||
</cl-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useCrud, useTable } from "@cool-vue/crud";
|
||||
import { nextTick, ref } from "vue";
|
||||
import { LogType } from "../dict";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
// 是否可见
|
||||
const visible = ref(false);
|
||||
|
||||
// 标题
|
||||
const title = ref("");
|
||||
|
||||
// cl-table
|
||||
const Table = useTable({
|
||||
autoHeight: false,
|
||||
columns: [
|
||||
{
|
||||
label: "#",
|
||||
type: "index"
|
||||
},
|
||||
{
|
||||
label: "请求",
|
||||
prop: "request",
|
||||
minWidth: 150,
|
||||
component: {
|
||||
name: "cl-code-json",
|
||||
props: {
|
||||
popover: true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "耗时",
|
||||
prop: "time",
|
||||
minWidth: 100,
|
||||
formatter(row) {
|
||||
return row.time + "ms";
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "结果",
|
||||
prop: "result",
|
||||
minWidth: 150,
|
||||
component: {
|
||||
name: "cl-code-json",
|
||||
props: {
|
||||
popover: true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "类型",
|
||||
prop: "type",
|
||||
minWidth: 100,
|
||||
dict: LogType
|
||||
},
|
||||
{
|
||||
label: "异常信息",
|
||||
prop: "error",
|
||||
minWidth: 150,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
label: "执行时间",
|
||||
prop: "createTime",
|
||||
minWidth: 160,
|
||||
sortable: "desc"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// cl-crud
|
||||
const Crud = useCrud({
|
||||
service: service.cloud.func.log
|
||||
});
|
||||
|
||||
// 打开
|
||||
function open(data: Eps.CloudFuncInfoEntity) {
|
||||
visible.value = true;
|
||||
title.value = `日志列表(${data.name})`;
|
||||
|
||||
nextTick(() => {
|
||||
Crud.value?.refresh({
|
||||
page: 1,
|
||||
infoId: data.id
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close
|
||||
});
|
||||
</script>
|
@ -1,21 +0,0 @@
|
||||
import type { ModuleConfig } from "/@/cool";
|
||||
import { CodeDeclare } from "./dict";
|
||||
import { addDeclare } from "/@/plugins/editor-monaco";
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
views: [
|
||||
{
|
||||
path: "/cloud/func/dev",
|
||||
meta: {
|
||||
label: "云函数开发"
|
||||
},
|
||||
component: () => import("./views/func/dev.vue")
|
||||
}
|
||||
],
|
||||
|
||||
onLoad() {
|
||||
addDeclare(CodeDeclare);
|
||||
}
|
||||
};
|
||||
};
|
@ -1,807 +0,0 @@
|
||||
export const CodeSnippets = {
|
||||
db: `import { BaseEntity } from '@cool-midway/core';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@Entity('xxx_xxx')
|
||||
export class xxxEntity extends BaseEntity {
|
||||
@Column({ comment: 'xxx' })
|
||||
xxx: string;
|
||||
}`,
|
||||
func: `import { CloudCrud } from '@cool-midway/cloud';
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
export class Xxx extends CloudCrud {
|
||||
async main() {
|
||||
this.setCurdOption({
|
||||
entity: 'xxx',
|
||||
api: ['add', 'delete', 'update', 'info', 'list', 'page']
|
||||
});
|
||||
}
|
||||
}`,
|
||||
req: `{
|
||||
"method": "xxx",
|
||||
"params": {
|
||||
|
||||
}
|
||||
}`
|
||||
};
|
||||
|
||||
export const CodeDeclare = `
|
||||
declare module 'typeorm' {
|
||||
export declare function Entity(options?: EntityOptions): ClassDecorator;
|
||||
export declare function Entity(name?: string, options?: EntityOptions): ClassDecorator;
|
||||
|
||||
export interface EntityOptions {
|
||||
/**
|
||||
* Table name.
|
||||
* If not specified then naming strategy will generate table name from entity name.
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* Specifies a default order by used for queries from this table when no explicit order by is specified.
|
||||
*/
|
||||
orderBy?: OrderByCondition | ((object: any) => OrderByCondition | any);
|
||||
/**
|
||||
* Table's database engine type (like "InnoDB", "MyISAM", etc).
|
||||
* It is used only during table creation.
|
||||
* If you update this value and table is already created, it will not change table's engine type.
|
||||
* Note that not all databases support this option.
|
||||
*/
|
||||
engine?: string;
|
||||
/**
|
||||
* Database name. Used in Mysql and Sql Server.
|
||||
*/
|
||||
database?: string;
|
||||
/**
|
||||
* Schema name. Used in Postgres and Sql Server.
|
||||
*/
|
||||
schema?: string;
|
||||
/**
|
||||
* Indicates if schema synchronization is enabled or disabled for this entity.
|
||||
* If it will be set to false then schema sync will and migrations ignore this entity.
|
||||
* By default schema synchronization is enabled for all entities.
|
||||
*/
|
||||
synchronize?: boolean;
|
||||
/**
|
||||
* If set to 'true' this option disables Sqlite's default behaviour of secretly creating
|
||||
* an integer primary key column named 'rowid' on table creation.
|
||||
* @see https://www.sqlite.org/withoutrowid.html.
|
||||
*/
|
||||
withoutRowid?: boolean;
|
||||
}
|
||||
|
||||
export declare function Column(options: ColumnOptions): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Describes all column's options.
|
||||
*/
|
||||
export interface ColumnOptions extends ColumnCommonOptions {
|
||||
/**
|
||||
* Column type. Must be one of the value from the ColumnTypes class.
|
||||
*/
|
||||
type?: ColumnType;
|
||||
/**
|
||||
* Column name in the database.
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* Column type's length. Used only on some column types.
|
||||
* For example type = "string" and length = "100" means that ORM will create a column with type varchar(100).
|
||||
*/
|
||||
length?: string | number;
|
||||
/**
|
||||
* Column type's display width. Used only on some column types in MySQL.
|
||||
* For example, INT(4) specifies an INT with a display width of four digits.
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* Indicates if column's value can be set to NULL.
|
||||
* Default value is "false".
|
||||
*/
|
||||
nullable?: boolean;
|
||||
/**
|
||||
* Indicates if column value is not updated by "save" operation.
|
||||
* It means you'll be able to write this value only when you first time insert the object.
|
||||
* Default value is "false".
|
||||
*
|
||||
* @deprecated Please use the \`update\` option instead. Careful, it takes
|
||||
* the opposite value to readonly.
|
||||
*
|
||||
*/
|
||||
readonly?: boolean;
|
||||
/**
|
||||
* Indicates if column value is updated by "save" operation.
|
||||
* If false, you'll be able to write this value only when you first time insert the object.
|
||||
* Default value is "true".
|
||||
*/
|
||||
update?: boolean;
|
||||
/**
|
||||
* Indicates if column is always selected by QueryBuilder and find operations.
|
||||
* Default value is "true".
|
||||
*/
|
||||
select?: boolean;
|
||||
/**
|
||||
* Indicates if column is inserted by default.
|
||||
* Default value is "true".
|
||||
*/
|
||||
insert?: boolean;
|
||||
/**
|
||||
* Default database value.
|
||||
*/
|
||||
default?: any;
|
||||
/**
|
||||
* ON UPDATE trigger. Works only for MySQL.
|
||||
*/
|
||||
onUpdate?: string;
|
||||
/**
|
||||
* Indicates if this column is a primary key.
|
||||
* Same can be achieved when @PrimaryColumn decorator is used.
|
||||
*/
|
||||
primary?: boolean;
|
||||
/**
|
||||
* Specifies if column's value must be unique or not.
|
||||
*/
|
||||
unique?: boolean;
|
||||
/**
|
||||
* Column comment. Not supported by all database types.
|
||||
*/
|
||||
comment?: string;
|
||||
/**
|
||||
* The precision for a decimal (exact numeric) column (applies only for decimal column), which is the maximum
|
||||
* number of digits that are stored for the values.
|
||||
*/
|
||||
precision?: number | null;
|
||||
/**
|
||||
* The scale for a decimal (exact numeric) column (applies only for decimal column), which represents the number
|
||||
* of digits to the right of the decimal point and must not be greater than precision.
|
||||
*/
|
||||
scale?: number;
|
||||
/**
|
||||
* Puts ZEROFILL attribute on to numeric column. Works only for MySQL.
|
||||
* If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to this column
|
||||
*/
|
||||
zerofill?: boolean;
|
||||
/**
|
||||
* Puts UNSIGNED attribute on to numeric column. Works only for MySQL.
|
||||
*/
|
||||
unsigned?: boolean;
|
||||
/**
|
||||
* Defines a column character set.
|
||||
* Not supported by all database types.
|
||||
*/
|
||||
charset?: string;
|
||||
/**
|
||||
* Defines a column collation.
|
||||
*/
|
||||
collation?: string;
|
||||
/**
|
||||
* Array of possible enumerated values.
|
||||
*/
|
||||
enum?: (string | number)[] | Object;
|
||||
/**
|
||||
* Exact name of enum
|
||||
*/
|
||||
enumName?: string;
|
||||
/**
|
||||
* If this column is primary key then this specifies the name for it.
|
||||
*/
|
||||
primaryKeyConstraintName?: string;
|
||||
/**
|
||||
* If this column is foreign key then this specifies the name for it.
|
||||
*/
|
||||
foreignKeyConstraintName?: string;
|
||||
/**
|
||||
* Generated column expression.
|
||||
*/
|
||||
asExpression?: string;
|
||||
/**
|
||||
* Generated column type.
|
||||
*/
|
||||
generatedType?: "VIRTUAL" | "STORED";
|
||||
/**
|
||||
* Identity column type. Supports only in Postgres 10+.
|
||||
*/
|
||||
generatedIdentity?: "ALWAYS" | "BY DEFAULT";
|
||||
/**
|
||||
* Return type of HSTORE column.
|
||||
* Returns value as string or as object.
|
||||
*/
|
||||
hstoreType?: "object" | "string";
|
||||
/**
|
||||
* Indicates if this column is an array.
|
||||
* Can be simply set to true or array length can be specified.
|
||||
* Supported only by postgres.
|
||||
*/
|
||||
array?: boolean;
|
||||
/**
|
||||
* Specifies a value transformer that is to be used to (un)marshal
|
||||
* this column when reading or writing to the database.
|
||||
*/
|
||||
transformer?: ValueTransformer | ValueTransformer[];
|
||||
/**
|
||||
* Spatial Feature Type (Geometry, Point, Polygon, etc.)
|
||||
*/
|
||||
spatialFeatureType?: string;
|
||||
/**
|
||||
* SRID (Spatial Reference ID (EPSG code))
|
||||
*/
|
||||
srid?: number;
|
||||
}
|
||||
|
||||
export declare function Index(options?: IndexOptions): ClassDecorator & PropertyDecorator;
|
||||
export interface IndexOptions {
|
||||
/**
|
||||
* Indicates if this composite index must be unique or not.
|
||||
*/
|
||||
unique?: boolean;
|
||||
/**
|
||||
* The SPATIAL modifier indexes the entire column and does not allow indexed columns to contain NULL values.
|
||||
* Works only in MySQL and PostgreSQL.
|
||||
*/
|
||||
spatial?: boolean;
|
||||
/**
|
||||
* The FULLTEXT modifier indexes the entire column and does not allow prefixing.
|
||||
* Works only in MySQL.
|
||||
*/
|
||||
fulltext?: boolean;
|
||||
/**
|
||||
* NULL_FILTERED indexes are particularly useful for indexing sparse columns, where most rows contain a NULL value.
|
||||
* In these cases, the NULL_FILTERED index can be considerably smaller and more efficient to maintain than
|
||||
* a normal index that includes NULL values.
|
||||
*
|
||||
* Works only in Spanner.
|
||||
*/
|
||||
nullFiltered?: boolean;
|
||||
/**
|
||||
* Fulltext parser.
|
||||
* Works only in MySQL.
|
||||
*/
|
||||
parser?: string;
|
||||
/**
|
||||
* Index filter condition.
|
||||
*/
|
||||
where?: string;
|
||||
/**
|
||||
* If true, the index only references documents with the specified field.
|
||||
* These indexes use less space but behave differently in some situations (particularly sorts).
|
||||
* This option is only supported for mongodb database.
|
||||
*/
|
||||
sparse?: boolean;
|
||||
/**
|
||||
* Builds the index in the background so that building an index an does not block other database activities.
|
||||
* This option is only supported for mongodb database.
|
||||
*/
|
||||
background?: boolean;
|
||||
/**
|
||||
* Specifies a time to live, in seconds.
|
||||
* This option is only supported for mongodb database.
|
||||
*/
|
||||
expireAfterSeconds?: number;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@cool-midway/core' {
|
||||
export declare abstract class BaseEntity extends CoolBaseEntity {
|
||||
id: number;
|
||||
createTime: Date;
|
||||
updateTime: Date;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@cool-midway/cloud' {
|
||||
export type ApiTypes = "add" | "delete" | "update" | "page" | "info" | "list";
|
||||
// Crud配置
|
||||
|
||||
export interface CurdOption {
|
||||
// 路由前缀,不配置默认是按Controller下的文件夹路径
|
||||
prefix?: string;
|
||||
// curd api接口
|
||||
api: ApiTypes[];
|
||||
// 分页查询配置
|
||||
pageQueryOp?: QueryOp | Function;
|
||||
// 非分页查询配置
|
||||
listQueryOp?: QueryOp | Function;
|
||||
// 插入参数
|
||||
insertParam?: Function;
|
||||
// 操作之前
|
||||
before?: Function;
|
||||
// info 忽略返回属性
|
||||
infoIgnoreProperty?: string[];
|
||||
// 实体
|
||||
entity: any;
|
||||
// 服务
|
||||
service?: any;
|
||||
// api标签
|
||||
urlTag?: {
|
||||
name: "ignoreToken" | string;
|
||||
url: ApiTypes[];
|
||||
};
|
||||
}
|
||||
export interface JoinOp {
|
||||
// 实体
|
||||
entity: any;
|
||||
// 别名
|
||||
alias: string;
|
||||
// 关联条件
|
||||
condition: string;
|
||||
// 关联类型
|
||||
type?: "innerJoin" | "leftJoin";
|
||||
}
|
||||
|
||||
// 字段匹配
|
||||
export interface FieldEq {
|
||||
// 字段
|
||||
column: string;
|
||||
// 请求参数
|
||||
requestParam: string;
|
||||
}
|
||||
// 查询配置
|
||||
export interface QueryOp {
|
||||
// 需要模糊查询的字段
|
||||
keyWordLikeFields?: string[];
|
||||
// 查询条件
|
||||
where?: Function;
|
||||
// 查询字段
|
||||
select?: string[];
|
||||
// 字段相等
|
||||
fieldEq?: string[] | FieldEq[];
|
||||
// 添加排序条件
|
||||
addOrderBy?: {};
|
||||
// 关联配置
|
||||
join?: JoinOp[];
|
||||
// 其他条件
|
||||
extend?: Function;
|
||||
}
|
||||
|
||||
// Controller 配置
|
||||
export interface ControllerOption {
|
||||
// crud配置 如果是字符串则为路由前缀,不配置默认是按Controller下的文件夹路径
|
||||
curdOption?: CurdOption & string;
|
||||
// 路由配置
|
||||
routerOptions?: {
|
||||
// 是否敏感
|
||||
sensitive?: boolean;
|
||||
// 路由中间件
|
||||
middleware?: MiddlewareParamArray;
|
||||
// 别名
|
||||
alias?: string[];
|
||||
// 描述
|
||||
description?: string;
|
||||
// 标签名称
|
||||
tagName?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块配置
|
||||
*/
|
||||
export interface ModuleConfig {
|
||||
/** 名称 */
|
||||
name: string;
|
||||
/** 描述 */
|
||||
description: string;
|
||||
/** 模块中间件 */
|
||||
middlewares?: MiddlewareParamArray;
|
||||
/** 全局中间件 */
|
||||
globalMiddlewares?: MiddlewareParamArray;
|
||||
/** 模块加载顺序,默认为0,值越大越优先加载 */
|
||||
order?: number;
|
||||
}
|
||||
export interface CoolConfig {
|
||||
/** 是否自动导入数据库 */
|
||||
initDB?: boolean;
|
||||
/** crud配置 */
|
||||
crud?: {
|
||||
/** 软删除 */
|
||||
softDelete: boolean;
|
||||
/** 分页查询每页条数 */
|
||||
pageSize: number;
|
||||
};
|
||||
/** elasticsearch配置 */
|
||||
es?: {
|
||||
nodes: string[];
|
||||
};
|
||||
/** pay */
|
||||
pay?: {
|
||||
/** 微信支付 */
|
||||
wx?: CoolWxPayConfig;
|
||||
/** 支付宝支付 */
|
||||
ali?: CoolAliPayConfig;
|
||||
};
|
||||
/** rpc */
|
||||
rpc?: CoolRpcConfig;
|
||||
/** redis */
|
||||
redis?: RedisConfig | RedisConfig[];
|
||||
/** 文件上传 */
|
||||
file?: {
|
||||
/** 上传模式 */
|
||||
mode: MODETYPE;
|
||||
/** 本地上传 文件地址前缀 */
|
||||
domain?: string;
|
||||
/** oss */
|
||||
oss?: OSSConfig;
|
||||
/** cos */
|
||||
cos?: COSConfig;
|
||||
/** qiniu */
|
||||
qiniu?: QINIUConfig;
|
||||
};
|
||||
/** IOT 配置 */
|
||||
iot: CoolIotConfig;
|
||||
}
|
||||
export interface CoolRpcConfig {
|
||||
/** 服务名称 */
|
||||
name: string;
|
||||
/** redis */
|
||||
redis: RedisConfig & RedisConfig[] & unknown;
|
||||
}
|
||||
export interface RedisConfig {
|
||||
/** host */
|
||||
host: string;
|
||||
/** password */
|
||||
password: string;
|
||||
/** port */
|
||||
port: number;
|
||||
/** db */
|
||||
db: number;
|
||||
}
|
||||
export declare enum MODETYPE {
|
||||
/** 本地 */
|
||||
LOCAL = "local",
|
||||
/** 云存储 */
|
||||
CLOUD = "cloud",
|
||||
/** 其他 */
|
||||
OTHER = "other"
|
||||
}
|
||||
export declare enum CLOUDTYPE {
|
||||
/** 阿里云存储 */
|
||||
OSS = "oss",
|
||||
/** 腾讯云存储 */
|
||||
COS = "cos",
|
||||
/** 七牛云存储 */
|
||||
QINIU = "qiniu"
|
||||
}
|
||||
/**
|
||||
* 上传模式
|
||||
*/
|
||||
export interface Mode {
|
||||
/** 模式 */
|
||||
mode: MODETYPE;
|
||||
/** 类型 */
|
||||
type: string;
|
||||
}
|
||||
/**
|
||||
* 模块配置
|
||||
*/
|
||||
export interface CoolFileConfig {
|
||||
/** 上传模式 */
|
||||
mode: MODETYPE;
|
||||
/** 阿里云oss 配置 */
|
||||
oss: OSSConfig;
|
||||
/** 腾讯云 cos配置 */
|
||||
cos: COSConfig;
|
||||
/** 七牛云 配置 */
|
||||
qiniu: QINIUConfig;
|
||||
/** 文件前缀 */
|
||||
domain: string;
|
||||
}
|
||||
/**
|
||||
* OSS 配置
|
||||
*/
|
||||
export interface OSSConfig {
|
||||
/** 阿里云accessKeyId */
|
||||
accessKeyId: string;
|
||||
/** 阿里云accessKeySecret */
|
||||
accessKeySecret: string;
|
||||
/** 阿里云oss的bucket */
|
||||
bucket: string;
|
||||
/** 阿里云oss的endpoint */
|
||||
endpoint: string;
|
||||
/** 阿里云oss的timeout */
|
||||
timeout: string;
|
||||
/** 签名失效时间,毫秒 */
|
||||
expAfter?: number;
|
||||
/** 文件最大的 size */
|
||||
maxSize?: number;
|
||||
}
|
||||
/**
|
||||
* COS 配置
|
||||
*/
|
||||
export interface COSConfig {
|
||||
/** 腾讯云accessKeyId */
|
||||
accessKeyId: string;
|
||||
/** 腾讯云accessKeySecret */
|
||||
accessKeySecret: string;
|
||||
/** 腾讯云cos的bucket */
|
||||
bucket: string;
|
||||
/** 腾讯云cos的区域 */
|
||||
region: string;
|
||||
/** 腾讯云cos的公网访问地址 */
|
||||
publicDomain: string;
|
||||
/** 上传持续时间 */
|
||||
durationSeconds?: number;
|
||||
/** 允许操作(上传)的对象前缀 */
|
||||
allowPrefix?: string;
|
||||
/** 密钥的权限列表 */
|
||||
allowActions?: string[];
|
||||
}
|
||||
export interface QINIUConfig {
|
||||
/** 七牛云accessKeyId */
|
||||
accessKeyId: string;
|
||||
/** 七牛云accessKeySecret */
|
||||
accessKeySecret: string;
|
||||
/** 七牛云cos的bucket */
|
||||
bucket: string;
|
||||
/** 七牛云cos的区域 */
|
||||
region: string;
|
||||
/** 七牛云cos的公网访问地址 */
|
||||
publicDomain: string;
|
||||
/** 上传地址 */
|
||||
uploadUrl?: string;
|
||||
/** 上传fileKey */
|
||||
fileKey?: string;
|
||||
}
|
||||
/**
|
||||
* 微信支付配置
|
||||
*/
|
||||
export interface CoolWxPayConfig {
|
||||
/** 直连商户申请的公众号或移动应用appid。 */
|
||||
appid: string;
|
||||
/** 商户号 */
|
||||
mchid: string;
|
||||
/** 可选参数 证书序列号 */
|
||||
serial_no?: string;
|
||||
/** 回调链接 */
|
||||
notify_url: string;
|
||||
/** 公钥 */
|
||||
publicKey: Buffer;
|
||||
/** 私钥 */
|
||||
privateKey: Buffer;
|
||||
/** 可选参数 认证类型,目前为WECHATPAY2-SHA256-RSA2048 */
|
||||
authType?: string;
|
||||
/** 可选参数 User-Agent */
|
||||
userAgent?: string;
|
||||
/** 可选参数 APIv3密钥 */
|
||||
key?: string;
|
||||
}
|
||||
/**
|
||||
* 支付宝支付配置
|
||||
*/
|
||||
export interface CoolAliPayConfig {
|
||||
/** 支付回调地址 */
|
||||
notifyUrl: string;
|
||||
/** 应用ID */
|
||||
appId: string;
|
||||
/**
|
||||
* 应用私钥字符串
|
||||
* RSA签名验签工具:https://docs.open.alipay.com/291/106097)
|
||||
* 密钥格式一栏请选择 “PKCS1(非JAVA适用)”
|
||||
*/
|
||||
privateKey: string;
|
||||
/** 签名类型 */
|
||||
signType?: "RSA2" | "RSA";
|
||||
/** 支付宝公钥(需要对返回值做验签时候必填) */
|
||||
alipayPublicKey?: string;
|
||||
/** 网关 */
|
||||
gateway?: string;
|
||||
/** 网关超时时间(单位毫秒,默认 5s) */
|
||||
timeout?: number;
|
||||
/** 是否把网关返回的下划线 key 转换为驼峰写法 */
|
||||
camelcase?: boolean;
|
||||
/** 编码(只支持 utf-8) */
|
||||
charset?: "utf-8";
|
||||
/** api版本 */
|
||||
version?: "1.0";
|
||||
urllib?: any;
|
||||
/** 指定private key类型, 默认: PKCS1, PKCS8: PRIVATE KEY, PKCS1: RSA PRIVATE KEY */
|
||||
keyType?: "PKCS1" | "PKCS8";
|
||||
/** 应用公钥证书文件路径 */
|
||||
appCertPath?: string;
|
||||
/** 应用公钥证书文件内容 */
|
||||
appCertContent?: string | Buffer;
|
||||
/** 应用公钥证书sn */
|
||||
appCertSn?: string;
|
||||
/** 支付宝根证书文件路径 */
|
||||
alipayRootCertPath?: string;
|
||||
/** 支付宝根证书文件内容 */
|
||||
alipayRootCertContent?: string | Buffer;
|
||||
/** 支付宝根证书sn */
|
||||
alipayRootCertSn?: string;
|
||||
/** 支付宝公钥证书文件路径 */
|
||||
alipayPublicCertPath?: string;
|
||||
/** 支付宝公钥证书文件内容 */
|
||||
alipayPublicCertContent?: string | Buffer;
|
||||
/** 支付宝公钥证书sn */
|
||||
alipayCertSn?: string;
|
||||
/** AES密钥,调用AES加解密相关接口时需要 */
|
||||
encryptKey?: string;
|
||||
/** 服务器地址 */
|
||||
wsServiceUrl?: string;
|
||||
}
|
||||
/**
|
||||
* IOT配置
|
||||
*/
|
||||
export interface CoolIotConfig {
|
||||
/** MQTT服务端口 */
|
||||
port: number;
|
||||
/** MQTT Websocket服务端口 */
|
||||
wsPort: number;
|
||||
/** redis 配置 mqtt cluster下必须要配置 */
|
||||
redis?: {
|
||||
/** host */
|
||||
host: string;
|
||||
/** port */
|
||||
port: number;
|
||||
/** password */
|
||||
password: string;
|
||||
/** db */
|
||||
db: number;
|
||||
};
|
||||
/** 发布消息配置 */
|
||||
publish?: PublishPacket;
|
||||
/** 认证 */
|
||||
auth?: {
|
||||
/** 用户 */
|
||||
username: string;
|
||||
/** 密码 */
|
||||
password: string;
|
||||
};
|
||||
/** 服务配置 */
|
||||
serve?: AedesOptions;
|
||||
}
|
||||
|
||||
export declare abstract class CloudCrud {
|
||||
ctx: any;
|
||||
curdOption: CurdOption;
|
||||
coolCloudDb: CoolCloudDb;
|
||||
coolConfig: CoolConfig;
|
||||
entity: Repository<any>;
|
||||
app: IMidwayApplication;
|
||||
req: CloudReq;
|
||||
coolEventManager: CoolEventManager;
|
||||
protected sqlParams: any;
|
||||
setCurdOption(curdOption: CurdOption): void;
|
||||
/**
|
||||
* 设置实体
|
||||
* @param entityModel
|
||||
*/
|
||||
setEntity(): Promise<void>;
|
||||
abstract main(req: CloudReq): Promise<void>;
|
||||
init(req: CloudReq): Promise<void>;
|
||||
/**
|
||||
* 参数安全性检查
|
||||
* @param params
|
||||
*/
|
||||
paramSafetyCheck(params: any): Promise<boolean>;
|
||||
/**
|
||||
* 非分页查询
|
||||
* @param query 查询条件
|
||||
* @param option 查询配置
|
||||
*/
|
||||
list(query: any): Promise<any>;
|
||||
/**
|
||||
* 执行SQL并获得分页数据
|
||||
* @param sql 执行的sql语句
|
||||
* @param query 分页查询条件
|
||||
* @param autoSort 是否自动排序
|
||||
*/
|
||||
sqlRenderPage(sql: any, query: any, autoSort?: boolean): Promise<{
|
||||
list: any;
|
||||
pagination: {
|
||||
page: number;
|
||||
size: number;
|
||||
total: number;
|
||||
};
|
||||
}>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @param connectionName 连接名
|
||||
*/
|
||||
page(query: any): Promise<{
|
||||
list: any;
|
||||
pagination: {
|
||||
page: number;
|
||||
size: number;
|
||||
total: number;
|
||||
};
|
||||
}>;
|
||||
/**
|
||||
* 获得查询个数的SQL
|
||||
* @param sql
|
||||
*/
|
||||
getCountSql(sql: any): string;
|
||||
/**
|
||||
* 操作entity获得分页数据,不用写sql
|
||||
* @param find QueryBuilder
|
||||
* @param query
|
||||
* @param autoSort
|
||||
* @param connectionName
|
||||
*/
|
||||
entityRenderPage(find: SelectQueryBuilder<any>, query: any, autoSort?: boolean): Promise<{
|
||||
list: any[];
|
||||
pagination: {
|
||||
page: number;
|
||||
size: number;
|
||||
total: number;
|
||||
};
|
||||
}>;
|
||||
/**
|
||||
* 检查排序
|
||||
* @param sort 排序
|
||||
* @returns
|
||||
*/
|
||||
private checkSort;
|
||||
/**
|
||||
* 原生查询
|
||||
* @param sql
|
||||
* @param params
|
||||
*/
|
||||
nativeQuery(sql: any, params?: any): Promise<any>;
|
||||
/**
|
||||
* 获得ORM管理
|
||||
* @param connectionName 连接名称
|
||||
*/
|
||||
getOrmManager(): import("../db/source").CoolDataSource;
|
||||
private before;
|
||||
/**
|
||||
* 插入参数值
|
||||
* @param curdOption 配置
|
||||
*/
|
||||
private insertParam;
|
||||
/**
|
||||
* 新增|修改|删除 之后的操作
|
||||
* @param data 对应数据
|
||||
*/
|
||||
modifyAfter(data: any, type: 'delete' | 'update' | 'add'): Promise<void>;
|
||||
/**
|
||||
* 新增|修改|删除 之前的操作
|
||||
* @param data 对应数据
|
||||
*/
|
||||
modifyBefore(data: any, type: 'delete' | 'update' | 'add'): Promise<void>;
|
||||
/**
|
||||
* 新增
|
||||
* @param param
|
||||
* @returns
|
||||
*/
|
||||
add(param: any): Promise<{
|
||||
id: any;
|
||||
}>;
|
||||
/**
|
||||
* 新增|修改
|
||||
* @param param 数据
|
||||
*/
|
||||
addOrUpdate(param: any | any[]): Promise<void>;
|
||||
/**
|
||||
* 删除
|
||||
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||
*/
|
||||
delete(ids: any): Promise<void>;
|
||||
/**
|
||||
* 软删除
|
||||
* @param ids 删除的ID数组
|
||||
* @param entity 实体
|
||||
*/
|
||||
softDelete(ids: string[], entity?: Repository<any>, userId?: string): Promise<void>;
|
||||
/**
|
||||
* 修改
|
||||
* @param param 数据
|
||||
*/
|
||||
update(param: any): Promise<void>;
|
||||
/**
|
||||
* 获得单个ID
|
||||
* @param id ID
|
||||
*/
|
||||
info(id: any): Promise<any>;
|
||||
/**
|
||||
* 构建查询配置
|
||||
* @param query 前端查询
|
||||
* @param option
|
||||
*/
|
||||
private getOptionFind;
|
||||
}
|
||||
}
|
||||
`;
|
@ -1,27 +0,0 @@
|
||||
export const Status = [
|
||||
{
|
||||
label: "启用",
|
||||
value: 1,
|
||||
type: "success"
|
||||
},
|
||||
{
|
||||
label: "禁用",
|
||||
value: 0,
|
||||
type: "danger"
|
||||
}
|
||||
];
|
||||
|
||||
export const LogType = [
|
||||
{
|
||||
label: "成功",
|
||||
value: 1,
|
||||
type: "success"
|
||||
},
|
||||
{
|
||||
label: "失败",
|
||||
value: 0,
|
||||
type: "danger"
|
||||
}
|
||||
];
|
||||
|
||||
export * from "./code";
|
@ -1,302 +0,0 @@
|
||||
<template>
|
||||
<cl-view-group ref="ViewGroup">
|
||||
<template #item-name="{ item }">
|
||||
{{ item.name }} - {{ item.tableName }} - {{ item.className }}</template
|
||||
>
|
||||
|
||||
<template #right>
|
||||
<div class="source" v-loading="loading">
|
||||
<div class="head">
|
||||
<el-button @click="refresh()">刷新</el-button>
|
||||
<el-button type="success" @click="edit()">添加</el-button>
|
||||
<el-button type="danger" @click="clear()">清空</el-button>
|
||||
</div>
|
||||
|
||||
<el-scrollbar class="scrollbar">
|
||||
<div class="list">
|
||||
<div class="item" v-for="(item, index) in list" :key="index">
|
||||
<cl-code-json :model-value="item" :height="150">
|
||||
<template #op>
|
||||
<el-button type="primary" size="small" @click="edit(item)"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button type="danger" size="small" @click="remove(item.id)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</cl-code-json>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty" v-if="list.length == 0">
|
||||
<el-empty :image-size="100" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
<div class="footer">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:current-page="pagination.page"
|
||||
:total="pagination.total"
|
||||
:page-size="pagination.size"
|
||||
@current-change="onCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<cl-form ref="Form" />
|
||||
</div>
|
||||
</template>
|
||||
</cl-view-group>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="cloud-db" setup>
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { reactive, ref } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { CodeSnippets, Status } from "../dict";
|
||||
import { useViewGroup } from "/@/plugins/view";
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
const { ViewGroup } = useViewGroup({
|
||||
label: "表",
|
||||
title: "数据列表",
|
||||
service: service.cloud.db,
|
||||
onEdit() {
|
||||
return {
|
||||
props: {
|
||||
labelWidth: "60px"
|
||||
},
|
||||
items: [
|
||||
{ label: "名称", prop: "name", required: true, component: { name: "el-input" } },
|
||||
{
|
||||
label: "内容",
|
||||
prop: "content",
|
||||
component: {
|
||||
name: "cl-editor-monaco",
|
||||
props: {
|
||||
language: "typescript",
|
||||
options: {
|
||||
fontSize: "16px"
|
||||
}
|
||||
}
|
||||
},
|
||||
value: CodeSnippets.db,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
label: "说明",
|
||||
prop: "readme",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
type: "textarea",
|
||||
rows: 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
value: 1,
|
||||
component: { name: "el-radio-group", options: Status },
|
||||
required: true
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
onSelect() {
|
||||
refresh({
|
||||
page: 1
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const Form = useForm();
|
||||
|
||||
// 加载中
|
||||
const loading = ref(false);
|
||||
|
||||
// 列表
|
||||
const list = ref<any[]>([]);
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
size: 20,
|
||||
total: 0
|
||||
});
|
||||
|
||||
// 请求参数
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 20
|
||||
};
|
||||
|
||||
// 统一请求
|
||||
function request(method: string, params?: any) {
|
||||
return service.cloud.db.data({
|
||||
id: ViewGroup.value?.selected?.id,
|
||||
method,
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新
|
||||
async function refresh(params?: any) {
|
||||
Object.assign(reqParams, params);
|
||||
|
||||
loading.value = true;
|
||||
|
||||
await request("page", reqParams)
|
||||
.then((res) => {
|
||||
list.value = res.list;
|
||||
Object.assign(pagination, res.pagination);
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 清空数据
|
||||
function clear() {
|
||||
ElMessageBox.confirm(
|
||||
`此操作将会清空表(${ViewGroup.value?.selected?.name})的所有数据,是否继续?`,
|
||||
"提示",
|
||||
{
|
||||
type: "warning"
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
request("clear")
|
||||
.then(() => {
|
||||
ElMessage.success("清空数据成功");
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
})
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
// 删除数据
|
||||
function remove(id: string) {
|
||||
ElMessageBox.confirm("此操作将会删除选择的数据,是否继续?", "提示", {
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
request("delete", {
|
||||
ids: [id]
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("删除数据成功");
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
})
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
// 编辑
|
||||
function edit(item?: any) {
|
||||
Form.value?.open({
|
||||
title: item ? "编辑数据" : "添加数据",
|
||||
props: {
|
||||
labelWidth: "60px"
|
||||
},
|
||||
items: [
|
||||
{
|
||||
label: "内容",
|
||||
prop: "content",
|
||||
component: {
|
||||
name: "cl-editor-monaco",
|
||||
props: {
|
||||
options: {
|
||||
fontSize: "16px"
|
||||
}
|
||||
}
|
||||
},
|
||||
value: `{}`,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
form: {
|
||||
content: JSON.stringify(item, null, 4)
|
||||
},
|
||||
on: {
|
||||
submit(data, { close, done }) {
|
||||
try {
|
||||
request(item ? "update" : "add", JSON.parse(data.content))
|
||||
.then(() => {
|
||||
ElMessage.success("保存成功");
|
||||
close();
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
done();
|
||||
});
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.toString());
|
||||
done();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 监听页数变化
|
||||
function onCurrentChange(index: number) {
|
||||
refresh({
|
||||
page: index
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.source {
|
||||
height: 100%;
|
||||
|
||||
.head {
|
||||
padding: 0 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
height: calc(100% - 92px);
|
||||
box-sizing: border-box;
|
||||
|
||||
.list {
|
||||
.item {
|
||||
padding: 0 10px 10px 10px;
|
||||
background-color: var(--el-bg-color);
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,382 +0,0 @@
|
||||
<template>
|
||||
<div class="func-dev">
|
||||
<div class="head">
|
||||
<el-icon class="back" @click="router.back">
|
||||
<back />
|
||||
</el-icon>
|
||||
|
||||
<span class="name">云函数({{ info.name }})</span>
|
||||
|
||||
<div class="btn">
|
||||
<el-tooltip content="重新加载函数代码,将会丢失当前未保存的编辑" effect="light">
|
||||
<el-button size="small" :loading="loading" @click="refresh(true)">
|
||||
刷新
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-button size="small" type="success" :loading="saving" @click="save">
|
||||
保存(S)
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="code">
|
||||
<cl-editor-monaco
|
||||
:ref="setRefs('code')"
|
||||
height="100%"
|
||||
language="typescript"
|
||||
:options="{
|
||||
fontSize: 18
|
||||
}"
|
||||
v-model="info.content"
|
||||
@change="
|
||||
(val) => {
|
||||
onCodeChange('devCode', val);
|
||||
}
|
||||
"
|
||||
@keydown="onCodeKeydown"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="debug">
|
||||
<el-row :gutter="10">
|
||||
<el-col :sm="12" :xs="24">
|
||||
<div class="card">
|
||||
<div class="h">
|
||||
<span>调用参数</span>
|
||||
<el-button
|
||||
size="small"
|
||||
type="success"
|
||||
:loading="running"
|
||||
@click="run"
|
||||
>
|
||||
运行(B)
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="c">
|
||||
<cl-editor-monaco
|
||||
:ref="setRefs('req')"
|
||||
height="100%"
|
||||
:options="{
|
||||
fontSize: 18
|
||||
}"
|
||||
v-model="debug.req"
|
||||
@keydown="onReqKeydown"
|
||||
@change="
|
||||
(val) => {
|
||||
onCodeChange('devReq', val);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :sm="12" :xs="24">
|
||||
<div class="card">
|
||||
<div class="h">
|
||||
<span>执行日志</span>
|
||||
<el-button size="small" @click="refs.logs?.open(info)">
|
||||
查看全部
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="c is-border">
|
||||
<el-scrollbar v-loading="running">
|
||||
<div class="c2">
|
||||
<template v-if="debug.log.time > 0">
|
||||
<div class="row">
|
||||
<span
|
||||
>执行方法:{{ debug.log.request?.method }}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span>执行时间:2023-02-16 01:23:00</span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span>执行状态:</span>
|
||||
<span class="ok" v-if="debug.log.type == 1"
|
||||
>成功</span
|
||||
>
|
||||
<span class="fail" v-else>失败</span>
|
||||
|
||||
<span style="margin-left: 20px">耗时:</span>
|
||||
<span>{{ debug.log.time }}ms</span>
|
||||
</div>
|
||||
|
||||
<div class="row">执行结果:</div>
|
||||
|
||||
<cl-code-json
|
||||
height="auto"
|
||||
:model-value="debug.log.result"
|
||||
v-if="debug.log.type == 1"
|
||||
/>
|
||||
|
||||
<div class="error" v-else>
|
||||
{{ debug.log.error }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-empty :image-size="80" />
|
||||
</template>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 日志 -->
|
||||
<func-logs :ref="setRefs('logs')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="cloud-func-dev" setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { storage, useCool } from "/@/cool";
|
||||
import { Back } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { CodeSnippets } from "../../dict";
|
||||
import FuncLogs from "../../components/func-logs.vue";
|
||||
|
||||
const { route, service, refs, setRefs, router } = useCool();
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 保存状态
|
||||
const saving = ref(false);
|
||||
|
||||
// 运行状态
|
||||
const running = ref(false);
|
||||
|
||||
// 调试
|
||||
const debug = reactive({
|
||||
req: "",
|
||||
log: {
|
||||
error: "",
|
||||
type: 0,
|
||||
time: 0,
|
||||
createTime: "",
|
||||
result: {},
|
||||
request: {
|
||||
method: ""
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 云函数详情
|
||||
const info = ref<Eps.CloudFuncInfoEntity>({
|
||||
content: ""
|
||||
});
|
||||
|
||||
// 获取云函数详情
|
||||
async function refresh(sync?: boolean) {
|
||||
loading.value = true;
|
||||
|
||||
await service.cloud.func.info
|
||||
.info({
|
||||
id: route.query.id
|
||||
})
|
||||
.then((res) => {
|
||||
if (!sync) {
|
||||
res.content = storage.get(`cloud.devCode-${res.id}`) || res.content;
|
||||
debug.req = storage.get(`cloud.devReq-${res.id}`) || CodeSnippets.req;
|
||||
}
|
||||
info.value = res;
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 保存
|
||||
async function save() {
|
||||
saving.value = true;
|
||||
|
||||
await refs.code.formatCode();
|
||||
|
||||
await service.cloud.func.info
|
||||
.update({
|
||||
id: info.value.id,
|
||||
content: info.value.content
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("保存成功");
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
// 运行
|
||||
async function run() {
|
||||
running.value = true;
|
||||
|
||||
await refs.req.formatCode();
|
||||
|
||||
try {
|
||||
await service.cloud.func.info
|
||||
.invoke({
|
||||
id: info.value.id,
|
||||
content: info.value.content,
|
||||
...JSON.parse(debug.req)
|
||||
})
|
||||
.then((res) => {
|
||||
debug.log = res;
|
||||
ElMessage.success("运行成功");
|
||||
})
|
||||
.catch((err) => {
|
||||
debug.log.time = 0;
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.toString());
|
||||
}
|
||||
|
||||
running.value = false;
|
||||
}
|
||||
|
||||
// 代码保存到本地
|
||||
function onCodeChange(name: string, value: string) {
|
||||
storage.set(`cloud.${name}-${info.value.id}`, value);
|
||||
}
|
||||
|
||||
// ctrk + s 保存
|
||||
function onCodeKeydown(e: KeyboardEvent) {
|
||||
if (e.ctrlKey && e.key.toLocaleLowerCase() == "s") {
|
||||
save();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// ctrk + b 运行
|
||||
function onReqKeydown(e: KeyboardEvent) {
|
||||
if (e.ctrlKey && e.key.toLocaleLowerCase() == "b") {
|
||||
run();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.func-dev {
|
||||
height: 100%;
|
||||
background-color: var(--el-bg-color);
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 5px;
|
||||
|
||||
.name {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.back {
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
font-size: 18px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
height: calc(100% - 40px);
|
||||
padding: 0 10px 10px 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.code {
|
||||
height: calc(100% - 320px);
|
||||
background-color: var(--el-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
height: 320px;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--el-bg-color);
|
||||
|
||||
.h {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
|
||||
.refresh {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin-left: 6px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c {
|
||||
height: calc(100% - 40px);
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
|
||||
&.is-border {
|
||||
border: 1px solid var(--el-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.c2 {
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
|
||||
.error {
|
||||
background-color: #fef0f0;
|
||||
color: var(--el-color-danger);
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 5px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.ok {
|
||||
color: var(--el-color-success);
|
||||
}
|
||||
|
||||
.fail {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,154 +0,0 @@
|
||||
<template>
|
||||
<cl-crud ref="Crud">
|
||||
<cl-row>
|
||||
<!-- 刷新按钮 -->
|
||||
<cl-refresh-btn />
|
||||
<!-- 新增按钮 -->
|
||||
<cl-add-btn />
|
||||
<!-- 删除按钮 -->
|
||||
<cl-multi-delete-btn />
|
||||
<!-- 筛选 -->
|
||||
<cl-filter label="状态">
|
||||
<cl-select :options="Status" prop="status" :width="120" />
|
||||
</cl-filter>
|
||||
<cl-flex1 />
|
||||
<!-- 关键字搜索 -->
|
||||
<cl-search-key placeholder="搜索名称" />
|
||||
</cl-row>
|
||||
|
||||
<cl-row>
|
||||
<!-- 数据表格 -->
|
||||
<cl-table ref="Table" />
|
||||
</cl-row>
|
||||
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<!-- 分页控件 -->
|
||||
<cl-pagination />
|
||||
</cl-row>
|
||||
|
||||
<!-- 新增、编辑 -->
|
||||
<cl-upsert ref="Upsert" />
|
||||
|
||||
<!-- 日志 -->
|
||||
<func-logs :ref="setRefs('logs')" />
|
||||
</cl-crud>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="cloud-func-info" setup>
|
||||
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { useCool } from "/@/cool";
|
||||
import { Status, CodeSnippets } from "../../dict";
|
||||
import FuncLogs from "../../components/func-logs.vue";
|
||||
|
||||
const { service, refs, setRefs, router } = useCool();
|
||||
|
||||
// cl-upsert
|
||||
const Upsert = useUpsert({
|
||||
props: {
|
||||
labelWidth: "60px"
|
||||
},
|
||||
items: [
|
||||
{ label: "名称", prop: "name", required: true, component: { name: "el-input" } },
|
||||
{
|
||||
label: "内容",
|
||||
prop: "content",
|
||||
component: {
|
||||
name: "cl-editor-monaco",
|
||||
props: {
|
||||
language: "typescript"
|
||||
},
|
||||
ref: setRefs("monaco")
|
||||
},
|
||||
value: CodeSnippets.func,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
label: "说明",
|
||||
prop: "readme",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
type: "textarea",
|
||||
rows: 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
value: 1,
|
||||
component: { name: "el-radio-group", options: Status },
|
||||
required: true
|
||||
}
|
||||
],
|
||||
async onSubmit(data, { next }) {
|
||||
const content = await refs.monaco.formatCode();
|
||||
|
||||
next({
|
||||
...data,
|
||||
content
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// cl-table
|
||||
const Table = useTable({
|
||||
columns: [
|
||||
{ type: "selection" },
|
||||
{ label: "名称", prop: "name", minWidth: 160 },
|
||||
{ label: "说明", prop: "readme", minWidth: 200, showOverflowTooltip: true },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
component: {
|
||||
name: "cl-switch"
|
||||
},
|
||||
minWidth: 150
|
||||
},
|
||||
{ label: "创建时间", prop: "createTime", minWidth: 160, sortable: "desc" },
|
||||
{ label: "更新时间", prop: "updateTime", minWidth: 160, sortable: "custom" },
|
||||
{
|
||||
type: "op",
|
||||
width: 360,
|
||||
buttons: [
|
||||
"edit",
|
||||
"delete",
|
||||
{
|
||||
label: "开发",
|
||||
type: "success",
|
||||
hidden: !(
|
||||
service.cloud.func.info._permission.info &&
|
||||
service.cloud.func.info._permission.invoke
|
||||
),
|
||||
onClick({ scope }) {
|
||||
router.push({
|
||||
path: "/cloud/func/dev",
|
||||
query: {
|
||||
id: scope.row.id
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "查看日志",
|
||||
hidden: !service.cloud.func.log._permission.page,
|
||||
onClick({ scope }) {
|
||||
refs.logs.open(scope.row);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// cl-crud
|
||||
const Crud = useCrud(
|
||||
{
|
||||
service: service.cloud.func.info
|
||||
},
|
||||
(app) => {
|
||||
app.refresh();
|
||||
}
|
||||
);
|
||||
</script>
|
@ -1,195 +0,0 @@
|
||||
<template>
|
||||
<div class="dp-config">
|
||||
<div class="head" v-show="t">
|
||||
<span>{{ t }}</span>
|
||||
<el-button text type="danger" @click="del" v-if="showDel">删除</el-button>
|
||||
</div>
|
||||
|
||||
<div class="tips" v-if="tips">
|
||||
<el-icon>
|
||||
<warning-filled />
|
||||
</el-icon>
|
||||
<span>{{ tips }}</span>
|
||||
</div>
|
||||
|
||||
<el-scrollbar class="scrollbar" v-if="visible">
|
||||
<div class="form">
|
||||
<cl-form inner ref="Form">
|
||||
<template #slot-required="{ scope }">
|
||||
<el-checkbox v-model="scope.required" @change="onRChange">必填</el-checkbox>
|
||||
</template>
|
||||
</cl-form>
|
||||
|
||||
<el-empty :image-size="100" v-show="!t" description="未选择组件" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { WarningFilled } from "@element-plus/icons-vue";
|
||||
import { useDp } from "../hooks";
|
||||
|
||||
const { mitt } = useCool();
|
||||
const { dp } = useDp();
|
||||
const Form = useForm();
|
||||
|
||||
const visible = ref(true);
|
||||
const t = ref("");
|
||||
const data = reactive<any>({});
|
||||
const tips = ref("");
|
||||
|
||||
// 是否组
|
||||
const isGroup = computed(() => data.isTemp);
|
||||
|
||||
// 是否显示删除套件
|
||||
const showDel = computed(() => {
|
||||
return isGroup.value || (!!dp.getGroup(data.id) && data.isDel === false);
|
||||
});
|
||||
|
||||
function refresh(options: any) {
|
||||
for (const i in data) {
|
||||
delete data[i];
|
||||
}
|
||||
|
||||
Object.assign(data, options);
|
||||
|
||||
const { title, items = [] } = options.config;
|
||||
|
||||
t.value = title || "未配置";
|
||||
tips.value = options.config.tips;
|
||||
|
||||
Form.value?.open({
|
||||
form: options.component.props,
|
||||
items,
|
||||
props: {
|
||||
labelPosition: "top"
|
||||
},
|
||||
op: {
|
||||
hidden: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onRChange(v: any) {
|
||||
if (isGroup.value) {
|
||||
data.component.props.children.forEach((e: any) => {
|
||||
e.component.props.required = v;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function del() {
|
||||
clear();
|
||||
dp.removeBy({
|
||||
id: dp.getGroup(data.id).id
|
||||
});
|
||||
}
|
||||
|
||||
function clear() {
|
||||
Form.value?.close();
|
||||
t.value = "";
|
||||
tips.value = "";
|
||||
}
|
||||
|
||||
let stop: any = null;
|
||||
|
||||
mitt.on("dp.setConfig", (options: any) => {
|
||||
visible.value = false;
|
||||
|
||||
if (stop) {
|
||||
stop();
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
visible.value = true;
|
||||
|
||||
nextTick(() => {
|
||||
refresh(options || {});
|
||||
|
||||
stop = watch(
|
||||
() => Form.value?.form,
|
||||
(val) => {
|
||||
if (val) {
|
||||
Object.assign(options.component.props, val);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
mitt.on("dp.clearConfig", clear);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dp-config {
|
||||
height: 100%;
|
||||
width: 350px;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 54px;
|
||||
font-size: 18px;
|
||||
color: #262626;
|
||||
padding: 0 15px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.tips {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background-color: #fff8d5;
|
||||
color: #ffbb00;
|
||||
margin: 10px 24px 0 24px;
|
||||
padding: 0 20px 0 4px;
|
||||
border-radius: 4px;
|
||||
|
||||
.el-icon {
|
||||
margin: 5px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
height: calc(100% - 55px);
|
||||
}
|
||||
|
||||
.form {
|
||||
padding: 16px 24px;
|
||||
box-sizing: border-box;
|
||||
|
||||
:deep(.form-label) {
|
||||
font-size: 16px;
|
||||
color: #000;
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
color: #bfbfbf;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,969 +0,0 @@
|
||||
<template>
|
||||
<div class="dp-demo">
|
||||
<el-scrollbar>
|
||||
<div class="group" v-for="(group, index) in tab.list" :key="index">
|
||||
<p class="label">{{ group.label }}</p>
|
||||
|
||||
<draggable
|
||||
v-model="group.children"
|
||||
class="list"
|
||||
item-key="label"
|
||||
:group="{
|
||||
name: 'A',
|
||||
pull: 'clone',
|
||||
put: false
|
||||
}"
|
||||
:sort="false"
|
||||
:clone="onClone"
|
||||
@end="onEnd"
|
||||
>
|
||||
<template #item="{ element: item }">
|
||||
<div class="item" @click="add(item)">
|
||||
<img :src="icons[item.name]" />
|
||||
<span>{{ item.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import { ElMessage } from "element-plus";
|
||||
import { cloneDeep, merge } from "lodash-es";
|
||||
import { reactive } from "vue";
|
||||
import Draggable from "vuedraggable/src/vuedraggable";
|
||||
import { useDp } from "../hooks";
|
||||
import { Dp } from "../types";
|
||||
import { useCool } from "/@/cool";
|
||||
import { uuid } from "/@/cool/utils";
|
||||
|
||||
const { mitt } = useCool();
|
||||
const { dp } = useDp();
|
||||
|
||||
// 图标
|
||||
const files: any = import.meta.glob("/src/modules/design/static/icon/*", {
|
||||
eager: true
|
||||
});
|
||||
|
||||
const icons = reactive<any>({});
|
||||
|
||||
for (const i in files) {
|
||||
icons[i.replace("/src/modules/design/static/icon/", "").replace(".png", "")] = files[i].default;
|
||||
}
|
||||
|
||||
// 基础
|
||||
const demo: Dp.DemoItem[] = [
|
||||
{
|
||||
label: "单行文字",
|
||||
name: "text"
|
||||
},
|
||||
{
|
||||
label: "多行文字",
|
||||
name: "textarea"
|
||||
},
|
||||
{
|
||||
label: "单项选择",
|
||||
name: "radio",
|
||||
component: {
|
||||
name: "demo-item",
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true,
|
||||
options: []
|
||||
}
|
||||
},
|
||||
config: {
|
||||
items: [
|
||||
{
|
||||
label: "选项",
|
||||
renderLabel: (
|
||||
<p class="form-label">
|
||||
选项 <span>最多200项,每项最多50个字</span>
|
||||
</p>
|
||||
),
|
||||
component: {
|
||||
name: "demo-select"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "多项选择",
|
||||
name: "checkbox",
|
||||
component: {
|
||||
name: "demo-item",
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true,
|
||||
options: []
|
||||
}
|
||||
},
|
||||
config: {
|
||||
items: [
|
||||
{
|
||||
label: "选项",
|
||||
renderLabel: (
|
||||
<p class="form-label">
|
||||
选项 <span>最多200项,每项最多50个字</span>
|
||||
</p>
|
||||
),
|
||||
component: {
|
||||
name: "demo-select"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "时间",
|
||||
name: "time",
|
||||
component: {
|
||||
name: "demo-item",
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true
|
||||
}
|
||||
},
|
||||
config: {
|
||||
items: [
|
||||
{
|
||||
label: "日期",
|
||||
prop: "format",
|
||||
value: "YYYY-MM-DD",
|
||||
component: {
|
||||
name: "el-radio-group",
|
||||
options: [
|
||||
{
|
||||
label: "年-月-日",
|
||||
value: "YYYY-MM-DD"
|
||||
},
|
||||
{
|
||||
label: "年-月-日 时:分",
|
||||
value: "YYYY-MM-DD HH:mm"
|
||||
},
|
||||
{
|
||||
label: "年-月-日 时:分:秒",
|
||||
value: "YYYY-MM-DD HH:mm:ss"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "时间区间",
|
||||
name: "time-range",
|
||||
component: {
|
||||
name: "demo-time-range",
|
||||
props: {
|
||||
labelStart: "开始时间",
|
||||
labelEnd: "结束时间",
|
||||
labelDuration: "时长",
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true,
|
||||
duration: true,
|
||||
durationType: "hour"
|
||||
}
|
||||
},
|
||||
config: {
|
||||
defs: [],
|
||||
items: [
|
||||
{
|
||||
label: "标题",
|
||||
renderLabel: (
|
||||
<p class="form-label">
|
||||
标题 <span>最多20字</span>
|
||||
</p>
|
||||
),
|
||||
prop: "labelStart",
|
||||
value: "开始时间",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 20,
|
||||
clearable: true,
|
||||
placeholder: "请输入"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "标题",
|
||||
renderLabel: (
|
||||
<p class="form-label">
|
||||
标题 <span>最多20字</span>
|
||||
</p>
|
||||
),
|
||||
prop: "labelEnd",
|
||||
value: "结束时间",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 20,
|
||||
clearable: true,
|
||||
placeholder: "请输入"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "提示文字",
|
||||
renderLabel: (
|
||||
<p class="form-label">
|
||||
提示文字 <span>最多50字</span>
|
||||
</p>
|
||||
),
|
||||
prop: "placeholder",
|
||||
value: "请输入",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 50,
|
||||
clearable: true,
|
||||
placeholder: "请输入"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "日期",
|
||||
prop: "format",
|
||||
value: "YYYY-MM-DD",
|
||||
component: {
|
||||
name: "el-radio-group",
|
||||
options: [
|
||||
{
|
||||
label: "年-月-日",
|
||||
value: "YYYY-MM-DD"
|
||||
},
|
||||
{
|
||||
label: "年-月-日 时:分",
|
||||
value: "YYYY-MM-DD HH:mm"
|
||||
},
|
||||
{
|
||||
label: "年-月-日 时:分:秒",
|
||||
value: "YYYY-MM-DD HH:mm:ss"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "是否计算时长",
|
||||
prop: "duration",
|
||||
component: {
|
||||
name: "demo-checkbox",
|
||||
props: {
|
||||
text: "开启"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "时长类型",
|
||||
prop: "durationType",
|
||||
value: "hour",
|
||||
hidden({ scope }) {
|
||||
return !scope.duration;
|
||||
},
|
||||
component: {
|
||||
name: "el-radio-group",
|
||||
options: [
|
||||
{
|
||||
label: "小时",
|
||||
value: "hour"
|
||||
},
|
||||
{
|
||||
label: "半天",
|
||||
value: "half_day"
|
||||
},
|
||||
{
|
||||
label: "天",
|
||||
value: "day"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "标题",
|
||||
renderLabel: (
|
||||
<p class="form-label">
|
||||
标题 <span>最多20字</span>
|
||||
</p>
|
||||
),
|
||||
hidden({ scope }) {
|
||||
return !scope.duration;
|
||||
},
|
||||
prop: "labelDuration",
|
||||
value: "时长",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 20,
|
||||
clearable: true,
|
||||
placeholder: "请输入"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "数字",
|
||||
name: "number",
|
||||
config: {
|
||||
items: [
|
||||
{
|
||||
label: "单位",
|
||||
prop: "unit",
|
||||
component: {
|
||||
name: "el-input"
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "数值类型",
|
||||
prop: "type",
|
||||
value: "int",
|
||||
component: {
|
||||
name: "el-radio-group",
|
||||
options: [
|
||||
{
|
||||
label: "整数",
|
||||
value: "int"
|
||||
},
|
||||
{
|
||||
label: "小数",
|
||||
value: "decimal"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "数值区间",
|
||||
prop: "range",
|
||||
value: [],
|
||||
component: {
|
||||
name: "demo-num-range"
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "默认值",
|
||||
prop: "defaultValue",
|
||||
component: {
|
||||
name: "el-input"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "金钱",
|
||||
name: "amount",
|
||||
config: {
|
||||
items: [
|
||||
{
|
||||
label: "单位",
|
||||
prop: "unit",
|
||||
component: {
|
||||
name: "el-input"
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "大写",
|
||||
prop: "uppercase",
|
||||
component: {
|
||||
name: "demo-checkbox",
|
||||
props: {
|
||||
text: "显示大写",
|
||||
tips: "(输入数字后自动显示大写)"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "地址",
|
||||
name: "address",
|
||||
getType: "auto",
|
||||
config: {
|
||||
defs: ["title"]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "附件",
|
||||
name: "file",
|
||||
component: {
|
||||
name: "demo-item"
|
||||
},
|
||||
config: {
|
||||
defs: ["title"]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "组合",
|
||||
name: "group",
|
||||
component: {
|
||||
name: "demo-group",
|
||||
props: {
|
||||
children: []
|
||||
}
|
||||
},
|
||||
config: {
|
||||
defs: ["title"]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "图片",
|
||||
name: "pic",
|
||||
component: {
|
||||
name: "demo-item"
|
||||
},
|
||||
config: {
|
||||
defs: ["title"]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function getDemo(name: string, data?: any) {
|
||||
const d = demo.find((e) => e.name == name);
|
||||
return merge(cloneDeep(d), data || {});
|
||||
}
|
||||
|
||||
// 数据
|
||||
const tab = reactive<{ list: { label: string; children: Dp.DemoItem[] }[] }>({
|
||||
list: [
|
||||
{
|
||||
label: "基础组件",
|
||||
children: demo
|
||||
},
|
||||
{
|
||||
label: "组合套件",
|
||||
children: [
|
||||
{
|
||||
label: "加班审批",
|
||||
name: "jiaban",
|
||||
group: [
|
||||
getDemo("radio", {
|
||||
label: "加班类型",
|
||||
required: true,
|
||||
component: {
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
label: "工作日加班",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: "休息日加班",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "节假日加班",
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}),
|
||||
getDemo("radio", {
|
||||
label: "加班补偿方式",
|
||||
required: true,
|
||||
component: {
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
label: "不计补偿",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: "加班工资",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "加班调休",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "加班工资或加班调休",
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
config: {
|
||||
items: [
|
||||
{},
|
||||
{
|
||||
label: "类型匹配",
|
||||
prop: "match",
|
||||
value: [],
|
||||
component: {
|
||||
name: "demo-jb-match"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
getDemo("time-range"),
|
||||
getDemo("textarea", {
|
||||
label: "加班原因",
|
||||
required: true
|
||||
}),
|
||||
getDemo("file"),
|
||||
{
|
||||
label: "流程",
|
||||
name: "approval-process",
|
||||
component: {
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "出差审批",
|
||||
name: "chuchai",
|
||||
group: [
|
||||
{
|
||||
label: "出差城市",
|
||||
required: true,
|
||||
name: "region",
|
||||
component: {
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "目的城市",
|
||||
required: true,
|
||||
name: "region",
|
||||
component: {
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true
|
||||
}
|
||||
}
|
||||
},
|
||||
getDemo("time-range", {
|
||||
required: true
|
||||
}),
|
||||
getDemo("textarea", {
|
||||
label: "出差事由"
|
||||
}),
|
||||
getDemo("file"),
|
||||
{
|
||||
label: "流程",
|
||||
name: "approval-process",
|
||||
component: {
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "补卡审批",
|
||||
name: "buka",
|
||||
group: [
|
||||
getDemo("time", {
|
||||
label: "补卡时间",
|
||||
required: true
|
||||
}),
|
||||
getDemo("textarea", {
|
||||
label: "补卡事由",
|
||||
required: true
|
||||
}),
|
||||
getDemo("pic"),
|
||||
{
|
||||
label: "流程",
|
||||
name: "approval-process",
|
||||
component: {
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "调班审批",
|
||||
name: "tiaoban",
|
||||
group: [
|
||||
getDemo("radio", {
|
||||
label: "实际申请人",
|
||||
required: true
|
||||
}),
|
||||
getDemo("time", {
|
||||
label: "调班日期",
|
||||
required: true
|
||||
}),
|
||||
getDemo("time", {
|
||||
label: "被调班日期",
|
||||
required: true
|
||||
}),
|
||||
{
|
||||
label: "调班班次"
|
||||
},
|
||||
getDemo("textarea", {
|
||||
label: "调班原因"
|
||||
}),
|
||||
getDemo("file"),
|
||||
{
|
||||
label: "流程",
|
||||
name: "approval-process",
|
||||
component: {
|
||||
props: {
|
||||
placeholder: "请选择",
|
||||
arrowIcon: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "请假审批",
|
||||
name: "qingjia",
|
||||
group: [
|
||||
getDemo("radio", {
|
||||
label: "请假类型",
|
||||
required: true,
|
||||
component: {
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
label: "事假",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "年假",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "病假",
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: "调休",
|
||||
value: 4
|
||||
},
|
||||
{
|
||||
label: "产假",
|
||||
value: 5
|
||||
},
|
||||
{
|
||||
label: "陪产假",
|
||||
value: 6
|
||||
},
|
||||
{
|
||||
label: "婚假",
|
||||
value: 7
|
||||
},
|
||||
{
|
||||
label: "丧假",
|
||||
value: 8
|
||||
},
|
||||
{
|
||||
label: "哺乳假",
|
||||
value: 9
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}),
|
||||
getDemo("time-range", {
|
||||
component: {
|
||||
props: {
|
||||
duration: true
|
||||
}
|
||||
}
|
||||
}),
|
||||
getDemo("textarea", {
|
||||
label: "请假事由",
|
||||
required: true
|
||||
}),
|
||||
getDemo("pic")
|
||||
],
|
||||
config: {
|
||||
tips: "适用于员工本人或他人代为发起请假申请"
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "外出审批",
|
||||
name: "waichu",
|
||||
group: [
|
||||
getDemo("time-range"),
|
||||
getDemo("textarea", {
|
||||
label: "外出事由",
|
||||
required: true
|
||||
}),
|
||||
getDemo("pic")
|
||||
],
|
||||
config: {
|
||||
tips: "适用于员工本人或他人代为发起外出申请"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 解析
|
||||
function parse(item: Dp.DemoItem) {
|
||||
function next(data: Dp.DemoItem, options: any = {}) {
|
||||
const { autoInc = true } = options;
|
||||
|
||||
const d: Dp.DemoItem = cloneDeep({
|
||||
...data,
|
||||
id: uuid()
|
||||
});
|
||||
|
||||
// 默认配置
|
||||
if (!d.config) {
|
||||
d.config = {};
|
||||
}
|
||||
|
||||
if (!d.config.items) {
|
||||
d.config.items = [];
|
||||
}
|
||||
|
||||
if (!d.config.defs) {
|
||||
d.config.defs = ["title", "placeholder"];
|
||||
}
|
||||
|
||||
d.config.title = d.label;
|
||||
|
||||
if (autoInc) {
|
||||
// 序号
|
||||
data._index = data._index !== undefined ? data._index + 1 : 0;
|
||||
|
||||
if (data._index > 0) {
|
||||
d.label += data._index;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认组件
|
||||
if (!d.component) {
|
||||
d.component = {};
|
||||
}
|
||||
if (!d.component.name) {
|
||||
d.component.name = "demo-item";
|
||||
}
|
||||
if (!d.component.props) {
|
||||
d.component.props = {};
|
||||
}
|
||||
if (!d.component.props.label) {
|
||||
d.component.props.label = d.label;
|
||||
}
|
||||
if (!d.component.props.name) {
|
||||
d.component.props.name = d.name;
|
||||
}
|
||||
if (d.required) {
|
||||
d.component.props.required = true;
|
||||
}
|
||||
|
||||
switch (d.getType) {
|
||||
// 系统自动获取
|
||||
case "auto":
|
||||
d.component.props.placeholder = "系统自动获取";
|
||||
break;
|
||||
}
|
||||
|
||||
// 表单项组件
|
||||
const items = [];
|
||||
|
||||
// 标题
|
||||
if (d.config.defs.includes("title")) {
|
||||
items.unshift({
|
||||
label: "标题",
|
||||
renderLabel: (
|
||||
<p class="form-label">
|
||||
标题 <span>最多20字</span>
|
||||
</p>
|
||||
),
|
||||
prop: "label",
|
||||
value: d.label,
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 20,
|
||||
clearable: true,
|
||||
placeholder: "请输入",
|
||||
disabled: d.config.disabled
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 占位符
|
||||
if (d.config.defs.includes("placeholder")) {
|
||||
items.push({
|
||||
label: "提示文字",
|
||||
renderLabel: (
|
||||
<p class="form-label">
|
||||
提示文字 <span>最多50字</span>
|
||||
</p>
|
||||
),
|
||||
prop: "placeholder",
|
||||
value: "请输入",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 50,
|
||||
clearable: true,
|
||||
placeholder: "请输入",
|
||||
disabled: d.config.disabled
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
d.config.items.unshift(...items);
|
||||
d.config.items.push({
|
||||
label: "验证",
|
||||
prop: "required",
|
||||
component: {
|
||||
name: "slot-required"
|
||||
}
|
||||
});
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
let v = null;
|
||||
|
||||
if (item.group) {
|
||||
if (dp.hasTemp()) {
|
||||
ElMessage.warning("请先删除已选套件");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const arr = item.group.map((e) => next(e, { autoInc: false }));
|
||||
arr.forEach((e: any) => {
|
||||
e.config.disabled = true;
|
||||
e.isDel = false;
|
||||
});
|
||||
v = next({
|
||||
label: item.label,
|
||||
name: item.name,
|
||||
isTemp: true,
|
||||
component: {
|
||||
name: "demo-group",
|
||||
props: {
|
||||
children: arr
|
||||
}
|
||||
},
|
||||
config: {
|
||||
defs: ["title"],
|
||||
...item.config
|
||||
}
|
||||
});
|
||||
} else {
|
||||
v = next(item);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
function getConfig(name: string) {
|
||||
let d = null;
|
||||
|
||||
tab.list.find((e) => {
|
||||
d = e.children.find((a) => a.name == name);
|
||||
|
||||
return !!d;
|
||||
});
|
||||
|
||||
return d ? parse(d)?.config : {};
|
||||
}
|
||||
|
||||
// 复制
|
||||
let _d: any = null;
|
||||
|
||||
function onClone(item: any) {
|
||||
// 记录复制的数据
|
||||
_d = parse(item);
|
||||
|
||||
// 发送拉取消息
|
||||
mitt.emit("dp.pull", _d);
|
||||
|
||||
return _d;
|
||||
}
|
||||
|
||||
function add(item: any) {
|
||||
dp.add(onClone(item));
|
||||
dp.scrollToBottom();
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
if (_d) {
|
||||
mitt.emit("dp.setActive", _d.id);
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getConfig
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dp-demo {
|
||||
width: 350px;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
|
||||
.group {
|
||||
.label {
|
||||
font-size: 15px;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 8px;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
width: calc(50% - 16px);
|
||||
margin: 0 8px 6px 8px;
|
||||
padding: 0 8px 0 18px;
|
||||
box-sizing: border-box;
|
||||
border: 1px dashed currentColor;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: #bfbfbf;
|
||||
|
||||
img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-right: 14px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.list {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<div class="demo-checkbox">
|
||||
<el-checkbox v-model="value" @change="onChange"
|
||||
>{{ text }} <span class="tips">{{ tips }}</span></el-checkbox
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="demo-checkbox" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
text: String,
|
||||
tips: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const value = ref(props.modelValue);
|
||||
|
||||
function onChange() {
|
||||
emit("update:modelValue", value.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.demo-checkbox {
|
||||
.tips {
|
||||
font-size: 12px;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,131 +0,0 @@
|
||||
<template>
|
||||
<div class="demo-group">
|
||||
<div class="head" v-show="label">{{ label }}</div>
|
||||
|
||||
<draggable
|
||||
v-model="list"
|
||||
class="list"
|
||||
tag="div"
|
||||
item-key="id"
|
||||
:group="{
|
||||
name: 'A',
|
||||
animation: 300,
|
||||
ghostClass: 'Ghost',
|
||||
dragClass: 'Drag',
|
||||
draggable: '.is-drag',
|
||||
put: isPut
|
||||
}"
|
||||
:clone="onClone"
|
||||
>
|
||||
<template #footer>
|
||||
<div class="tips">可拖入多个组件<span>(不包含组合组件)</span></div>
|
||||
</template>
|
||||
|
||||
<template #item="{ element: item, index }">
|
||||
<div
|
||||
class="item"
|
||||
:class="{
|
||||
active: dp.form.active == item.id
|
||||
}"
|
||||
@click.stop="dp.toDet(item)"
|
||||
>
|
||||
<el-icon
|
||||
class="close"
|
||||
@click.stop="remove(index)"
|
||||
v-show="dp.form.active == item.id && item.isDel !== false"
|
||||
>
|
||||
<close-bold />
|
||||
</el-icon>
|
||||
|
||||
<component
|
||||
:is="item.component.name"
|
||||
:data="item"
|
||||
v-bind="item.component.props"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="demo-group">
|
||||
import { ref, watch } from "vue";
|
||||
import Draggable from "vuedraggable/src/vuedraggable";
|
||||
import { CloseBold } from "@element-plus/icons-vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useDp } from "../../hooks";
|
||||
|
||||
const props = defineProps({
|
||||
label: String,
|
||||
children: Array
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:children"]);
|
||||
|
||||
const { mitt } = useCool();
|
||||
const { dp } = useDp();
|
||||
|
||||
const list = ref<any[]>(props.children || []);
|
||||
const isPut = ref(true);
|
||||
|
||||
function remove(index: number) {
|
||||
dp.clearConfig(list.value[index].id);
|
||||
list.value?.splice(index, 1);
|
||||
}
|
||||
|
||||
function onClone(data: any) {
|
||||
mitt.emit("dp.pull", data);
|
||||
return data;
|
||||
}
|
||||
|
||||
mitt.on("dp.setActive", (id: string) => {
|
||||
const d = list.value?.find((e) => e.id == id);
|
||||
if (d) {
|
||||
dp.toDet(d);
|
||||
}
|
||||
});
|
||||
|
||||
mitt.on("dp.pull", (d) => {
|
||||
isPut.value = d?.component.name != "demo-group";
|
||||
});
|
||||
|
||||
watch(
|
||||
list,
|
||||
(val) => {
|
||||
emit("update:children", val);
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.demo-group {
|
||||
background-color: #fff;
|
||||
|
||||
.head {
|
||||
line-height: 40px;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list {
|
||||
background-color: #d9effe;
|
||||
|
||||
.tips {
|
||||
color: #8c8c8c;
|
||||
font-size: 12px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.item {
|
||||
&:nth-last-child(2) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,172 +0,0 @@
|
||||
<template>
|
||||
<div class="demo-item" :class="[{ 'is-required': required }, `demo-item--${name}`]">
|
||||
<span class="label">{{ label }}</span>
|
||||
<div class="value">
|
||||
<slot>
|
||||
<template v-if="name == 'file'">
|
||||
<div class="upload">
|
||||
<el-icon>
|
||||
<link />
|
||||
</el-icon>
|
||||
上传附件
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="name == 'pic'">
|
||||
<div class="upload">
|
||||
<el-icon>
|
||||
<plus />
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<span class="placeholder">{{ placeholder }}</span>
|
||||
<el-icon v-if="arrowIcon" class="arrow-right">
|
||||
<arrow-right />
|
||||
</el-icon>
|
||||
</template>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="demo-item" setup>
|
||||
import { ArrowRight, Plus, Link } from "@element-plus/icons-vue";
|
||||
|
||||
defineProps({
|
||||
label: String,
|
||||
name: String,
|
||||
required: Boolean,
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请输入"
|
||||
},
|
||||
arrowIcon: Boolean
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.demo-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 40px;
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
width: 110px;
|
||||
flex-shrink: 0;
|
||||
padding-left: 12px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex: 1;
|
||||
padding: 0 12px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
.placeholder,
|
||||
.arrow-right {
|
||||
color: #e5e5e5;
|
||||
}
|
||||
}
|
||||
|
||||
&--textarea {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
.label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.value {
|
||||
.placeholder {
|
||||
display: block;
|
||||
height: 55px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--file,
|
||||
&--pic {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
.label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.upload {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
border: 1px dashed #e5e5e5;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 24px;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
padding-bottom: 10px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
&--file {
|
||||
.upload {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
font-size: 12px;
|
||||
|
||||
.el-icon {
|
||||
color: #000;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-required {
|
||||
.label {
|
||||
&::before {
|
||||
content: "*";
|
||||
color: red;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<div class="num-range">
|
||||
<el-input-number v-model="v[0]" :max="v[1]" @change="onChange" />
|
||||
<span>至</span>
|
||||
<el-input-number v-model="v[1]" :min="v[0]" @change="onChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="demo-num-range">
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Array
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const v = ref<any[]>(props.modelValue || []);
|
||||
|
||||
function onChange() {
|
||||
emit("update:modelValue", v.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.num-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.el-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,53 +0,0 @@
|
||||
<template>
|
||||
<div class="demo-select">
|
||||
<div class="item" v-for="(item, index) in Form?.form.options" :key="index">
|
||||
<el-input v-model="item.label" :placeholder="`请输入选项${index + 1}`" />
|
||||
<el-icon @click="add(index)"><circle-plus /></el-icon>
|
||||
<el-icon @click="del(index)"><remove /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="demo-select" setup>
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import { CirclePlus, Remove } from "@element-plus/icons-vue";
|
||||
import { isEmpty } from "lodash-es";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
const Form = useForm();
|
||||
|
||||
function add(index: number) {
|
||||
Form.value?.form.options.splice(index + 1, 0, {
|
||||
label: ""
|
||||
});
|
||||
}
|
||||
|
||||
function del(index: number) {
|
||||
Form.value?.form.options.splice(index, 1);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (isEmpty(Form.value?.form.options)) {
|
||||
add(0);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.el-input {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
margin: 0 5px;
|
||||
font-size: 16px;
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<div class="demo-time-range">
|
||||
<demo-item :label="labelStart" :placeholder="placeholder" :required="required" arrow-icon />
|
||||
<demo-item :label="labelEnd" :placeholder="placeholder" :required="required" arrow-icon />
|
||||
<demo-item :label="`${labelDuration}(小时)`" v-if="duration"></demo-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="demo-time-range" setup>
|
||||
defineProps({
|
||||
labelStart: String,
|
||||
labelEnd: String,
|
||||
labelDuration: String,
|
||||
required: Boolean,
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请选择"
|
||||
},
|
||||
arrowIcon: Boolean,
|
||||
duration: Boolean
|
||||
});
|
||||
</script>
|
@ -1,440 +0,0 @@
|
||||
<template>
|
||||
<el-scrollbar>
|
||||
<div class="dp-wrap">
|
||||
<dp-demo :ref="setRefs('demo')" />
|
||||
|
||||
<div class="dp-device">
|
||||
<div class="device">
|
||||
<div class="nav">设计页</div>
|
||||
|
||||
<el-scrollbar class="scrollbar" :ref="setRefs('scrollbar')">
|
||||
<draggable
|
||||
v-model="form.list"
|
||||
class="list"
|
||||
tag="div"
|
||||
item-key="id"
|
||||
:group="{
|
||||
name: 'A',
|
||||
animation: 300,
|
||||
ghostClass: 'Ghost',
|
||||
dragClass: 'Drag',
|
||||
draggable: '.is-drag'
|
||||
}"
|
||||
:clone="onClone"
|
||||
>
|
||||
<template #item="{ element: item, index }">
|
||||
<div
|
||||
class="item"
|
||||
:class="{
|
||||
active: form.active == item.id
|
||||
}"
|
||||
@click="toDet(item)"
|
||||
>
|
||||
<el-icon
|
||||
class="close"
|
||||
@click.stop="remove(index)"
|
||||
v-show="form.active == item.id"
|
||||
>
|
||||
<close-bold />
|
||||
</el-icon>
|
||||
|
||||
<!-- 组合 -->
|
||||
<demo-group
|
||||
:data="item"
|
||||
v-bind="item.component.props"
|
||||
v-model:children="item.component.props.children"
|
||||
v-if="item.component.props.children"
|
||||
/>
|
||||
|
||||
<!-- 基础元素 -->
|
||||
<component
|
||||
:is="item.component.name"
|
||||
:data="item"
|
||||
v-bind="item.component.props"
|
||||
v-else
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="tips">点击或者拖动组件添加</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dp-config />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, provide, reactive, nextTick } from "vue";
|
||||
import Draggable from "vuedraggable/src/vuedraggable";
|
||||
import { CloseBold } from "@element-plus/icons-vue";
|
||||
import { storage, useCool } from "/@/cool";
|
||||
import { isArray, isEmpty } from "lodash-es";
|
||||
import DpConfig from "./config.vue";
|
||||
import DpDemo from "./demo.vue";
|
||||
import { Dp } from "../types";
|
||||
|
||||
const { mitt, refs, setRefs } = useCool();
|
||||
|
||||
const form = reactive<{ active: string; list: any[] }>({
|
||||
active: "",
|
||||
list: []
|
||||
});
|
||||
|
||||
// 组件配置详情
|
||||
function toDet(item: any) {
|
||||
if (item) {
|
||||
form.active = item.id;
|
||||
mitt.emit("dp.setConfig", item);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加组件
|
||||
function add(data: any) {
|
||||
if (data) {
|
||||
const arr = isArray(data) ? data : [data];
|
||||
form.list.push(...arr);
|
||||
toDet(arr[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// 清空组件配置
|
||||
function clearConfig(id?: string) {
|
||||
if (form.active == id || !id) {
|
||||
mitt.emit("dp.clearConfig");
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
function remove(index: number) {
|
||||
clearConfig(form.list[index].id);
|
||||
|
||||
const d = form.list[index];
|
||||
|
||||
if (d) {
|
||||
if (d.isTemp) {
|
||||
// 添加套件中自定义的组件
|
||||
const arr = d.component.props.children.filter((e: any) => e.isDel !== false);
|
||||
|
||||
if (!isEmpty(arr)) {
|
||||
add(arr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form.list.splice(index, 1);
|
||||
}
|
||||
|
||||
// 删除方式2
|
||||
function removeBy({ id, index }: any) {
|
||||
if (id) {
|
||||
index = form.list.findIndex((e) => e.id == id);
|
||||
}
|
||||
|
||||
remove(index);
|
||||
}
|
||||
|
||||
// 清空列表
|
||||
function clear() {
|
||||
clearConfig();
|
||||
form.list = [];
|
||||
}
|
||||
|
||||
// 复制
|
||||
function onClone(data: any) {
|
||||
mitt.emit("dp.pull", data);
|
||||
return data;
|
||||
}
|
||||
|
||||
// 设置选中
|
||||
function setActive(id: string) {
|
||||
const d = form.list.find((e) => e.id == id);
|
||||
if (d) {
|
||||
toDet(d);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据 prop 获取列表中的信息
|
||||
function get(prop: string) {
|
||||
let d = null;
|
||||
|
||||
form.list.forEach((e) => {
|
||||
if (e.prop == prop) {
|
||||
return (d = e);
|
||||
} else {
|
||||
if (e.component.name == "demo-group") {
|
||||
e.component.props.children.forEach((a: any) => {
|
||||
if (a.prop == prop) {
|
||||
return (d = a);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
// 获取列表
|
||||
function getData() {
|
||||
function deep(arr: any[]): any {
|
||||
return arr.map((e) => {
|
||||
const isGroup = e.component.name == "demo-group" || e.name == "group";
|
||||
|
||||
const d = {
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
label: e.component.props.label
|
||||
};
|
||||
|
||||
if (isGroup) {
|
||||
return {
|
||||
...d,
|
||||
isGroup,
|
||||
children: deep(e.component.props.children || [])
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...d,
|
||||
prop: e.prop,
|
||||
name: e.name,
|
||||
getType: e.getType,
|
||||
props: e.component.props
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return deep(form.list);
|
||||
}
|
||||
|
||||
// 获取组
|
||||
function getGroup(id: string) {
|
||||
let d = null;
|
||||
|
||||
form.list.forEach((a) => {
|
||||
if (a.isTemp) {
|
||||
if (a.id == id) {
|
||||
d = a;
|
||||
} else {
|
||||
a.component.props.children.forEach((e: any) => {
|
||||
if (e.id == id) {
|
||||
d = a;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
// 是否套件
|
||||
function hasTemp() {
|
||||
return !!form.list.find((e) => e.isTemp);
|
||||
}
|
||||
|
||||
// 保存草稿
|
||||
function saveDraft() {
|
||||
storage.set(
|
||||
"design.pageCode",
|
||||
form.list.map((e) => {
|
||||
return {
|
||||
...e,
|
||||
config: undefined
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 获取草稿
|
||||
function getDraft() {
|
||||
const list: Dp.DemoItem[] = storage.get("design.pageCode") || [];
|
||||
|
||||
form.list = list.map((e) => {
|
||||
e.config = refs.demo.getConfig(e.name);
|
||||
return e;
|
||||
});
|
||||
|
||||
toDet(form.list[0]);
|
||||
}
|
||||
|
||||
// 滚动到底
|
||||
function scrollToBottom() {
|
||||
nextTick(() => {
|
||||
refs.scrollbar.scrollTo(0, 9999);
|
||||
});
|
||||
}
|
||||
|
||||
// 监听选中事件
|
||||
mitt.on("dp.setActive", setActive);
|
||||
|
||||
const dp = {
|
||||
form,
|
||||
get,
|
||||
getGroup,
|
||||
getData,
|
||||
toDet,
|
||||
setActive,
|
||||
add,
|
||||
remove,
|
||||
removeBy,
|
||||
clear,
|
||||
hasTemp,
|
||||
clearConfig,
|
||||
saveDraft,
|
||||
scrollToBottom
|
||||
};
|
||||
|
||||
provide("dp", dp);
|
||||
defineExpose(dp);
|
||||
|
||||
onMounted(() => {
|
||||
getDraft();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.Ghost {
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.dp-wrap {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
min-height: 700px;
|
||||
background-color: #edf0f3;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
color: #000;
|
||||
|
||||
.dp-device {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.device {
|
||||
position: relative;
|
||||
height: 667px;
|
||||
width: 360px;
|
||||
overflow: hidden;
|
||||
border-radius: 20px;
|
||||
background-color: #f7f8fa;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 54px;
|
||||
font-size: 18px;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
height: 612px;
|
||||
width: 100%;
|
||||
|
||||
.list {
|
||||
height: 100%;
|
||||
padding-bottom: 20px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
margin-bottom: 6px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
color: #fff;
|
||||
z-index: 9;
|
||||
background-color: var(--color-primary);
|
||||
padding: 1px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
|
||||
&.sortable-ghost {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 40px;
|
||||
width: 100%;
|
||||
padding: 0 18px;
|
||||
box-sizing: border-box;
|
||||
border: 1px dashed currentColor;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: #bfbfbf;
|
||||
background-color: #fff;
|
||||
opacity: 0.8;
|
||||
|
||||
img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-right: 14px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
[class^="demo-"] {
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
|
||||
.placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 2px solid var(--color-primary);
|
||||
box-sizing: border-box;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
padding: 10px 0 20px 0;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,7 +0,0 @@
|
||||
import type { ModuleConfig } from "/@/cool";
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
components: Object.values(import.meta.glob("./components/demo/*"))
|
||||
};
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
import { inject } from "vue";
|
||||
import { Dp } from "../types";
|
||||
|
||||
export function useDp() {
|
||||
const dp = inject("dp") as Dp.Provide;
|
||||
|
||||
return { dp };
|
||||
}
|
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1019 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 927 B |
Before Width: | Height: | Size: 988 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 783 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.5 KiB |
43
src/modules/design/types/index.d.ts
vendored
@ -1,43 +0,0 @@
|
||||
export declare namespace Dp {
|
||||
interface DemoItem {
|
||||
label: string;
|
||||
name?: string;
|
||||
required?: boolean;
|
||||
getType?: "auto";
|
||||
component?: {
|
||||
name?: string;
|
||||
props?: {
|
||||
children?: any[];
|
||||
[key: string]: any;
|
||||
};
|
||||
};
|
||||
config?: {
|
||||
defs?: string[];
|
||||
tips?: string;
|
||||
disabled?: boolean;
|
||||
items?: ClForm.Item[];
|
||||
[key: string]: any;
|
||||
};
|
||||
group?: DemoItem[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface Provide {
|
||||
form: {
|
||||
[key: string]: any;
|
||||
};
|
||||
get(prop: string): any;
|
||||
getGroup(id: string): any;
|
||||
getData(): any[];
|
||||
toDet(item: any): void;
|
||||
setActive(id: string): void;
|
||||
add(data: any): void;
|
||||
remove(index: number): void;
|
||||
removeBy(options: { id?: string; index?: number }): void;
|
||||
clear(): boolean;
|
||||
hasTemp(): boolean;
|
||||
clearConfig(id?: string): void;
|
||||
saveDraft(): void;
|
||||
scrollToBottom(): void;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<div class="form">
|
||||
<div class="container">
|
||||
<dp :ref="setRefs('dp')" />
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<el-button @click="clear">清空</el-button>
|
||||
<el-button type="info" @click="save">保存草稿</el-button>
|
||||
|
||||
<cl-editor-preview title="代码预览" name="monaco" :ref="setRefs('preview')">
|
||||
<el-button type="success" @click="create">生成代码</el-button>
|
||||
</cl-editor-preview>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useCool } from "/@/cool";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import Dp from "../components/index.vue";
|
||||
|
||||
const { refs, setRefs } = useCool();
|
||||
|
||||
function save() {
|
||||
refs.dp.saveDraft();
|
||||
ElMessage.success("保存草稿成功");
|
||||
}
|
||||
|
||||
function create() {
|
||||
refs.preview.open(refs.dp.getData());
|
||||
}
|
||||
|
||||
function clear() {
|
||||
ElMessageBox.confirm("是否清空列表所有数据?", "提示", {
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
refs.dp.clear();
|
||||
})
|
||||
.catch(() => null);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form {
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
min-width: 1300px;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.container {
|
||||
height: calc(100% - 80px);
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 20px;
|
||||
height: 80px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #ebeef5;
|
||||
z-index: 9;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,60 +0,0 @@
|
||||
import mqtt from "mqtt/dist/mqtt.min";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
let client: mqtt.MqttClient;
|
||||
|
||||
export function useMqtt() {
|
||||
const { mitt } = useCool();
|
||||
|
||||
function send(id: string, text: string) {
|
||||
client?.publish(id, text);
|
||||
}
|
||||
|
||||
function subscribe(id: string) {
|
||||
console.log("[iot] mqtt subscribe", id);
|
||||
|
||||
client?.subscribe(`${id}@admin`, function (err: string) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function connect() {
|
||||
// 断开
|
||||
disconnect();
|
||||
|
||||
// 连接
|
||||
client = mqtt.connect("ws://127.0.0.1:8083");
|
||||
|
||||
if (client) {
|
||||
client.on("connect", function () {
|
||||
console.log("[iot] mqtt connect");
|
||||
});
|
||||
|
||||
client.on("message", function (topic: string, message: string) {
|
||||
mitt.emit("iot.message", {
|
||||
id: topic.split("@")[0],
|
||||
message: message.toString()
|
||||
});
|
||||
});
|
||||
|
||||
client.on("error", function (err: string) {
|
||||
console.error(err);
|
||||
client?.reconnect();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
client?.end();
|
||||
}
|
||||
|
||||
return {
|
||||
client,
|
||||
connect,
|
||||
disconnect,
|
||||
subscribe,
|
||||
send
|
||||
};
|
||||
}
|
Before Width: | Height: | Size: 933 B |
@ -1,474 +0,0 @@
|
||||
<template>
|
||||
<cl-view-group ref="ViewGroup">
|
||||
<template #item="{ item, selected }">
|
||||
<div class="device-item" :class="{ 'is-active': selected.id == item.id }">
|
||||
<div class="icon">
|
||||
<cl-avatar shape="square" :src="item.icon || DeviceIcon" />
|
||||
</div>
|
||||
|
||||
<div class="det">
|
||||
<p class="name">{{ item.name }}</p>
|
||||
<p class="text">
|
||||
{{ item.uniqueId }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="status"
|
||||
:class="{
|
||||
'is-on': item.status
|
||||
}"
|
||||
>
|
||||
{{ item.status ? "在线" : "离线" }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<div class="message" v-loading="loading">
|
||||
<!-- 消息列表 -->
|
||||
<el-scrollbar class="list" :ref="setRefs('scrollbar')" @scroll="onScroll">
|
||||
<ul>
|
||||
<li v-for="(item, index) in list" :key="index">
|
||||
<div
|
||||
class="item"
|
||||
:class="{
|
||||
'is-right': item.type == 0
|
||||
}"
|
||||
>
|
||||
<div class="icon">
|
||||
<cl-avatar
|
||||
:size="36"
|
||||
shape="square"
|
||||
:src="
|
||||
item.type == 0
|
||||
? user.info?.headImg
|
||||
: ViewGroup?.selected?.icon || DeviceIcon
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="det"
|
||||
@contextmenu="
|
||||
(e) => {
|
||||
onContextMenu(e, item);
|
||||
}
|
||||
"
|
||||
>
|
||||
<div class="content">
|
||||
<span class="is-text">
|
||||
{{ item.data }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="date">
|
||||
{{ dayjs(item.createTime).format("YYYY-MM-DD HH:mm:ss") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="empty" v-if="list.length == 0">
|
||||
<el-empty :image-size="100" description="暂无消息" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
<!-- 底部 -->
|
||||
<div class="footer">
|
||||
<div class="input">
|
||||
<el-input
|
||||
v-model="value"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
resize="none"
|
||||
:autosize="{
|
||||
minRows: 4,
|
||||
maxRows: 10
|
||||
}"
|
||||
placeholder="输入内容"
|
||||
/>
|
||||
<el-button type="success" @click="send" :disabled="!value">发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</cl-view-group>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="iot-device" setup>
|
||||
import { ContextMenu } from "@cool-vue/crud";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { debounce, orderBy } from "lodash-es";
|
||||
import { computed, nextTick, onActivated, ref } from "vue";
|
||||
import { useMqtt } from "../hooks";
|
||||
import { useBase } from "/$/base";
|
||||
import { useCool } from "/@/cool";
|
||||
import dayjs from "dayjs";
|
||||
import { onBeforeRouteLeave } from "vue-router";
|
||||
import { useViewGroup } from "/@/plugins/view";
|
||||
import DeviceIcon from "../static/icon/device.png";
|
||||
|
||||
const { service, refs, setRefs, mitt } = useCool();
|
||||
const { copy } = useClipboard();
|
||||
const { user } = useBase();
|
||||
const mqtt = useMqtt();
|
||||
|
||||
const { ViewGroup } = useViewGroup({
|
||||
label: "设备",
|
||||
title: "消息列表",
|
||||
service: service.iot.device,
|
||||
onEdit(item) {
|
||||
return {
|
||||
width: "600px",
|
||||
items: [
|
||||
{
|
||||
label: "设备名称",
|
||||
prop: "name",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 20,
|
||||
clearable: true
|
||||
}
|
||||
},
|
||||
required: true
|
||||
},
|
||||
{
|
||||
label: "设备ID",
|
||||
prop: "uniqueId",
|
||||
component: {
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 30,
|
||||
clearable: true,
|
||||
disabled: !!item?.id
|
||||
}
|
||||
},
|
||||
required: true
|
||||
},
|
||||
{
|
||||
label: "设备图标",
|
||||
prop: "icon",
|
||||
component: {
|
||||
name: "cl-upload"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
async onSelect(item) {
|
||||
mqtt.subscribe(item.uniqueId);
|
||||
|
||||
await refresh({
|
||||
page: 1,
|
||||
deviceId: item.id
|
||||
});
|
||||
|
||||
scrollToBottom();
|
||||
}
|
||||
});
|
||||
|
||||
// 加载中
|
||||
const loading = ref(false);
|
||||
|
||||
// 输入值
|
||||
const value = ref("");
|
||||
|
||||
// 消息列表
|
||||
const list = ref<Eps.IotMessageEntity>([]);
|
||||
|
||||
// 设备id
|
||||
const uniqueId = computed(() => ViewGroup.value?.selected?.uniqueId);
|
||||
|
||||
// 参数
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 20
|
||||
};
|
||||
|
||||
// 是否加载完
|
||||
let loaded = false;
|
||||
|
||||
// 刷新
|
||||
async function refresh(params?: any) {
|
||||
loading.value = true;
|
||||
|
||||
Object.assign(reqParams, {
|
||||
order: "createTime",
|
||||
sort: "desc",
|
||||
...params
|
||||
});
|
||||
|
||||
await service.iot.message
|
||||
.page(reqParams)
|
||||
.then((res) => {
|
||||
const arr = orderBy(res.list, "createTime");
|
||||
|
||||
// 保留列表滚动条位置
|
||||
if (reqParams.page == 1) {
|
||||
list.value = arr;
|
||||
} else {
|
||||
const s = refs.scrollbar.wrapRef.querySelector("ul");
|
||||
const h = s.clientHeight;
|
||||
|
||||
list.value.unshift(...arr);
|
||||
|
||||
nextTick(() => {
|
||||
refs.scrollbar.scrollTo(0, s.clientHeight - h);
|
||||
});
|
||||
}
|
||||
|
||||
// 是否加载完
|
||||
loaded = res.pagination.total <= list.value.length;
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 滚动到底部
|
||||
const scrollToBottom = debounce(() => {
|
||||
nextTick(() => {
|
||||
refs.scrollbar?.wrapRef?.scroll({
|
||||
top: 100000 + Math.random(),
|
||||
behavior: "smooth"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 监听滚动
|
||||
function onScroll({ scrollTop }: { scrollTop: number }) {
|
||||
if (scrollTop == 0 && !loaded) {
|
||||
refresh({
|
||||
page: reqParams.page + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
function send() {
|
||||
service.iot.mqtt
|
||||
.publish({
|
||||
uniqueId: uniqueId.value,
|
||||
data: value.value
|
||||
})
|
||||
.then(() => {
|
||||
append({
|
||||
data: value.value,
|
||||
type: 0
|
||||
});
|
||||
value.value = "";
|
||||
});
|
||||
}
|
||||
|
||||
// 追加消息
|
||||
function append(data: Eps.IotMessageEntity) {
|
||||
list.value.push({
|
||||
createTime: new Date(),
|
||||
...data
|
||||
});
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// 右键菜单
|
||||
function onContextMenu(e: Event, item: Eps.IotMessageEntity) {
|
||||
ContextMenu.open(e, {
|
||||
hover: {
|
||||
target: "content"
|
||||
},
|
||||
list: [
|
||||
{
|
||||
label: "复制",
|
||||
callback(done) {
|
||||
copy(item.data || "");
|
||||
ElMessage.success("复制成功");
|
||||
done();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 监听消息
|
||||
function onMessage({ id, message }: any) {
|
||||
if (uniqueId.value == id) {
|
||||
append({
|
||||
type: 1,
|
||||
data: message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onActivated(() => {
|
||||
mqtt.connect();
|
||||
mitt.on("iot.message", onMessage);
|
||||
});
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
mqtt.disconnect();
|
||||
mitt.off("iot.message");
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-item {
|
||||
display: flex;
|
||||
padding: 15px 10px;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.det {
|
||||
flex: 1;
|
||||
|
||||
.name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
|
||||
&::before {
|
||||
display: block;
|
||||
content: "";
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 100%;
|
||||
background-color: currentColor;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&.is-on {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:not(.is-active):hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.list {
|
||||
flex: 1;
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
|
||||
ul {
|
||||
& > li {
|
||||
list-style: none;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.det {
|
||||
.date {
|
||||
font-size: 12px;
|
||||
margin: 6px 0 0 0;
|
||||
padding-left: 5px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.content {
|
||||
.is-text {
|
||||
display: inline-block;
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 0 8px 8px 8px;
|
||||
max-width: 400px;
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-right {
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.icon {
|
||||
margin-left: 10px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.det {
|
||||
text-align: right;
|
||||
|
||||
.content {
|
||||
.is-text {
|
||||
border-radius: 8px 0 8px 8px;
|
||||
background-color: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 10px;
|
||||
background-color: var(--el-bg-color);
|
||||
|
||||
.input {
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
.el-button {
|
||||
margin-left: 10px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
208
yarn.lock
@ -1366,11 +1366,6 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
before-after-hook@^2.2.0:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c"
|
||||
@ -1381,15 +1376,6 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
bl@^4.0.2:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
|
||||
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
|
||||
dependencies:
|
||||
buffer "^5.5.0"
|
||||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
boolbase@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
@ -1432,14 +1418,6 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||
|
||||
buffer@^5.5.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
|
||||
@ -1571,14 +1549,6 @@ commander@^9:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
|
||||
integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==
|
||||
|
||||
commist@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commist/-/commist-1.1.0.tgz#17811ec6978f6c15ee4de80c45c9beb77cee35d5"
|
||||
integrity sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==
|
||||
dependencies:
|
||||
leven "^2.1.0"
|
||||
minimist "^1.1.0"
|
||||
|
||||
compute-scroll-into-view@^1.0.20:
|
||||
version "1.0.20"
|
||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43"
|
||||
@ -1589,16 +1559,6 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||
|
||||
concat-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1"
|
||||
integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^3.0.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
convert-source-map@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
@ -1646,7 +1606,7 @@ dayjs@^1.11.10, dayjs@^1.11.3:
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
|
||||
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
||||
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
|
||||
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
@ -1712,16 +1672,6 @@ dom7@^3.0.0:
|
||||
dependencies:
|
||||
ssr-window "^3.0.0-alpha.1"
|
||||
|
||||
duplexify@^4.1.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0"
|
||||
integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==
|
||||
dependencies:
|
||||
end-of-stream "^1.4.1"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^3.1.1"
|
||||
stream-shift "^1.0.0"
|
||||
|
||||
eastasianwidth@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
|
||||
@ -1771,13 +1721,6 @@ emoji-regex@^9.2.2:
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
|
||||
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
|
||||
|
||||
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
engine.io-client@~6.5.2:
|
||||
version "6.5.3"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.5.3.tgz#4cf6fa24845029b238f83c628916d9149c399bc5"
|
||||
@ -2312,7 +2255,7 @@ glob@^10.3.10:
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
path-scurry "^1.10.1"
|
||||
|
||||
glob@^7.0.0, glob@^7.1.3, glob@^7.1.6:
|
||||
glob@^7.0.0, glob@^7.1.3:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
@ -2418,14 +2361,6 @@ hasown@^2.0.0, hasown@^2.0.1:
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
|
||||
help-me@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/help-me/-/help-me-3.0.0.tgz#9803c81b5f346ad2bce2c6a0ba01b82257d319e8"
|
||||
integrity sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==
|
||||
dependencies:
|
||||
glob "^7.1.6"
|
||||
readable-stream "^3.6.0"
|
||||
|
||||
html-tags@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce"
|
||||
@ -2443,11 +2378,6 @@ i18next@^20.4.0:
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.0"
|
||||
|
||||
ieee754@^1.1.13:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
ignore@^5.2.0, ignore@^5.2.4:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
|
||||
@ -2484,7 +2414,7 @@ inflight@^1.0.4:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@^2.0.3, inherits@^2.0.4:
|
||||
inherits@2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
@ -2680,11 +2610,6 @@ jackspeak@^2.3.5:
|
||||
optionalDependencies:
|
||||
"@pkgjs/parseargs" "^0.11.0"
|
||||
|
||||
js-sdsl@4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
|
||||
integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -2738,11 +2663,6 @@ keyv@^4.5.3:
|
||||
dependencies:
|
||||
json-buffer "3.0.1"
|
||||
|
||||
leven@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
|
||||
integrity sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==
|
||||
|
||||
levn@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
|
||||
@ -2902,11 +2822,6 @@ minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.1.0, minimist@^1.2.5:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0":
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
|
||||
@ -2933,38 +2848,6 @@ monaco-editor@0.36.0:
|
||||
pin-github-action "^1.8.0"
|
||||
shelljs "^0.8.5"
|
||||
|
||||
mqtt-packet@^6.8.0:
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/mqtt-packet/-/mqtt-packet-6.10.0.tgz#c8b507832c4152e3e511c0efa104ae4a64cd418f"
|
||||
integrity sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==
|
||||
dependencies:
|
||||
bl "^4.0.2"
|
||||
debug "^4.1.1"
|
||||
process-nextick-args "^2.0.1"
|
||||
|
||||
mqtt@^4.3.7:
|
||||
version "4.3.8"
|
||||
resolved "https://registry.yarnpkg.com/mqtt/-/mqtt-4.3.8.tgz#b8cc9a6eb5e4e0cb6eea699f24cd70dd7b228f1d"
|
||||
integrity sha512-2xT75uYa0kiPEF/PE0VPdavmEkoBzMT/UL9moid0rAvlCtV48qBwxD62m7Ld/4j8tSkIO1E/iqRl/S72SEOhOw==
|
||||
dependencies:
|
||||
commist "^1.0.0"
|
||||
concat-stream "^2.0.0"
|
||||
debug "^4.1.1"
|
||||
duplexify "^4.1.1"
|
||||
help-me "^3.0.0"
|
||||
inherits "^2.0.3"
|
||||
lru-cache "^6.0.0"
|
||||
minimist "^1.2.5"
|
||||
mqtt-packet "^6.8.0"
|
||||
number-allocator "^1.0.9"
|
||||
pump "^3.0.0"
|
||||
readable-stream "^3.6.0"
|
||||
reinterval "^1.1.0"
|
||||
rfdc "^1.3.0"
|
||||
split2 "^3.1.0"
|
||||
ws "^7.5.5"
|
||||
xtend "^4.0.2"
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
@ -3024,14 +2907,6 @@ nth-check@^2.1.1:
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
|
||||
number-allocator@^1.0.9:
|
||||
version "1.0.14"
|
||||
resolved "https://registry.yarnpkg.com/number-allocator/-/number-allocator-1.0.14.tgz#1f2e32855498a7740dcc8c78bed54592d930ee4d"
|
||||
integrity sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==
|
||||
dependencies:
|
||||
debug "^4.3.1"
|
||||
js-sdsl "4.3.0"
|
||||
|
||||
object-inspect@^1.13.1:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
|
||||
@ -3052,7 +2927,7 @@ object.assign@^4.1.5:
|
||||
has-symbols "^1.0.3"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
once@^1.3.0, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
@ -3212,24 +3087,11 @@ prismjs@^1.23.0:
|
||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12"
|
||||
integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==
|
||||
|
||||
process-nextick-args@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||
@ -3240,15 +3102,6 @@ queue-microtask@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||
|
||||
readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
|
||||
version "3.6.2"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
|
||||
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
string_decoder "^1.1.1"
|
||||
util-deprecate "^1.0.1"
|
||||
|
||||
readdirp@~3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||
@ -3278,11 +3131,6 @@ regexp.prototype.flags@^1.5.2:
|
||||
es-errors "^1.3.0"
|
||||
set-function-name "^2.0.1"
|
||||
|
||||
reinterval@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/reinterval/-/reinterval-1.1.0.tgz#3361ecfa3ca6c18283380dd0bb9546f390f5ece7"
|
||||
integrity sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
@ -3312,11 +3160,6 @@ reusify@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||
|
||||
rfdc@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f"
|
||||
integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==
|
||||
|
||||
rimraf@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||
@ -3373,11 +3216,6 @@ safe-array-concat@^1.1.0:
|
||||
has-symbols "^1.0.3"
|
||||
isarray "^2.0.5"
|
||||
|
||||
safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-regex-test@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377"
|
||||
@ -3545,13 +3383,6 @@ source-map@^0.7.4:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
||||
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
|
||||
|
||||
split2@^3.1.0:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f"
|
||||
integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==
|
||||
dependencies:
|
||||
readable-stream "^3.0.0"
|
||||
|
||||
ssf@~0.11.2:
|
||||
version "0.11.2"
|
||||
resolved "https://registry.yarnpkg.com/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c"
|
||||
@ -3569,12 +3400,8 @@ store@^2.0.12:
|
||||
resolved "https://registry.yarnpkg.com/store/-/store-2.0.12.tgz#8c534e2a0b831f72b75fc5f1119857c44ef5d593"
|
||||
integrity sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==
|
||||
|
||||
stream-shift@^1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b"
|
||||
integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
name string-width-cjs
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -3619,13 +3446,6 @@ string.prototype.trimstart@^1.0.7:
|
||||
define-properties "^1.2.0"
|
||||
es-abstract "^1.22.1"
|
||||
|
||||
string_decoder@^1.1.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
@ -3795,11 +3615,6 @@ typed-array-length@^1.0.4:
|
||||
is-typed-array "^1.1.13"
|
||||
possible-typed-array-names "^1.0.0"
|
||||
|
||||
typedarray@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
|
||||
|
||||
typescript@^5.2.2:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
|
||||
@ -3845,7 +3660,7 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2:
|
||||
util-deprecate@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
@ -3984,6 +3799,7 @@ word@~0.3.0:
|
||||
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
name wrap-ansi-cjs
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
@ -4006,11 +3822,6 @@ wrappy@1:
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
ws@^7.5.5:
|
||||
version "7.5.9"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
|
||||
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
|
||||
|
||||
ws@~8.11.0:
|
||||
version "8.11.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
|
||||
@ -4039,11 +3850,6 @@ xmlhttprequest-ssl@~2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
|
||||
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
|
||||
|
||||
xtend@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||
|
||||
y18n@^5.0.5:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
|