diff --git a/README.md b/README.md
index 9cdeb14af08e80e48461b96fa83f49f96fdd997f..40d9225362f865aeef0d9d8d3611827151a6f0bc 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,10 @@
iStock Shell v0.1
一个金融数据查询命令终端
它简单、高效、灵活,并很容易定制自己的金融数据查询工具。
+
+ 演示
+ 文档
+
---
@@ -22,7 +26,7 @@
#### 技术栈
-`svelte + typescript + vite`
+`typescript + svelte + vite`
### 内置功能
@@ -37,14 +41,13 @@
### 迭代计划
-**03月04日-03月10日:**
+**03月18日-03月24日:**
| 任务项目 | 描述 | 该迭代是否上线 |
| :--------------: | :----------------------------: | :------------: |
| 金融网站导航命令 | 用命令获取对应分类金融网站列表 | 是 |
-| 添加命令别名 | 命令输入返回后可添加命令别名 | 是 |
-**03月11日-03月17日预计任务项:**
+**03月25日-03月31日预计任务项:**
暂无安排
diff --git a/docs/.vitepress/config/theme-nav.ts b/docs/.vitepress/config/theme-nav.ts
index eaa52a4d0c18e129fa6180f3934c0cb2bbd683b6..fbc1a6419ef2e73b5ae54f04f00ee411d40d5058 100644
--- a/docs/.vitepress/config/theme-nav.ts
+++ b/docs/.vitepress/config/theme-nav.ts
@@ -36,7 +36,7 @@ export default [
{
text: '更多',
items: [
- { text: '更新日志', link: 'https://gitee.com/xcbclc/istock-shell', target: '_blank' },
+ { text: '更新日志', link: 'https://gitee.com/xcbclc/istock-shell/releases', target: '_blank' },
/*{ text: '沟通交流', link: 'https://share.istock.red/' },*/
{
text: '博客',
diff --git a/docs/use/quick-start/easy-to-use.md b/docs/use/quick-start/easy-to-use.md
index 95255890042937914ce950b8c1902d20ec9e81c3..8251cd0b60148a2ed3ed5d1a57589f2251a01685 100644
--- a/docs/use/quick-start/easy-to-use.md
+++ b/docs/use/quick-start/easy-to-use.md
@@ -50,3 +50,17 @@
图例待补充
通过上述步骤,您应该能够开始使用`iStock Shell`进行基本的数据查询和分析。随着对命令的熟悉,您将能够发挥出`iStock Shell`的更多潜力,满足您对金融数据查询和分析的需求。
+
+## 命令别名
+
+有时候,我们需要频繁输入的命令可能会很长,每次都输入会降低效率。这时候,给这些长命令取一个别名就很有用。
+
+举个例子,假设你经常需要输入这样一段命令:`lshqsj -gpmc 贵州茅台 -ksrq 20100101 -sjzq 月 | tb gplzt -x 日期 -y1 开盘,收盘 -y2 最高,最低`,
+这段命令有点长,如果每次都要输入,未免太费事了。我们可以给这个命令取一个别名,比如叫做`行情茅台`。
+设置别名的方法很简单:
+1. 在命令输出区域,鼠标右键选中`添加别名`菜单,或者使用快捷键`ctrl+alt+a`。
+2. 弹出别名设置菜单,设置别名为`行情茅台`。
+
+设置成功后,下次你只需要在命令输入框中输入`:行情`,按`tab`键就能在自动补全列表选择`行情茅台`别名,选择该别名后就会输入对应的命令了。
+
+通过设置命令别名,你可以更轻松地使用iStock Shell,不再需要反复输入那些繁长的命令,提高你的工作效率。
diff --git a/src/packages/cli/src/action/cmd-init.mjs b/src/packages/cli/src/action/cmd-init.mjs
index ef789438374d4d6e1f039f2ff8b39b8833c0d530..41a843781bb00d6860b27137f6ab81a2b73b4b17 100644
--- a/src/packages/cli/src/action/cmd-init.mjs
+++ b/src/packages/cli/src/action/cmd-init.mjs
@@ -23,7 +23,8 @@ export default async () => {
{
type: 'input',
name: 'name',
- message: '您期望命令相关文件名为?',
+ message: '您期望命令相关文件名为?(文件名用-符号分割)',
+
validate: (input) => {
if (!input) return false;
return nameReg.test(input);
@@ -34,6 +35,7 @@ export default async () => {
const templatePath = path.resolve(cwdPath, './src/packages/cli/src/template/cmd');
const outputDir = path.resolve(cwdPath, `./src/worker/domains/${domain}`);
const className = toClassName(name);
+ const instanceName = className[0].toLowerCase() + className.slice(1);
const fileName = toFileName(name);
const dirPath = path.resolve(outputDir, fileName);
@@ -60,8 +62,9 @@ export default async () => {
const renderedContent = ejs.render(templateContent, {
name,
className,
+ classAlias: instanceName,
fileName,
- instanceName: className[0].toLowerCase() + className.slice(1),
+ instanceName,
modelName: toModelName(name),
});
fs.writeFileSync(filePath, renderedContent);
diff --git a/src/packages/cli/src/template/cmd/tpl.controller.ejs b/src/packages/cli/src/template/cmd/tpl.controller.ejs
index 20ac02738098dca078ba79bb9cfe78d6195c31e1..2cdf97fda9993baf580d3804797680d03016093b 100644
--- a/src/packages/cli/src/template/cmd/tpl.controller.ejs
+++ b/src/packages/cli/src/template/cmd/tpl.controller.ejs
@@ -4,7 +4,7 @@ import { <%= className %>Service } from './<%= fileName %>.service';
import cmdJson from './<%= fileName %>.cmd';
@Controller({
- alias: '<%= fileName %>',
+ alias: '<%= classAlias %>',
})
export class <%= className %>Controller {
constructor(private readonly <%= instanceName %>Service: <%= className %>Service) {}
diff --git a/src/packages/cli/src/utils/name.mjs b/src/packages/cli/src/utils/name.mjs
index de0b49da493cf9ccf59082e38b4ccf8f2fb3ea4f..64af352eb1e7922979fb3f5eb8e78c2432defa1d 100644
--- a/src/packages/cli/src/utils/name.mjs
+++ b/src/packages/cli/src/utils/name.mjs
@@ -4,7 +4,7 @@
* @returns {*}
*/
export function toFileName(str) {
- if (!str) return;
+ if (!str) return '';
// 使用正则表达式将驼峰命名转换为破折号分隔
return str.replace(/[A-Z]/g, (match, index) => {
// 如果匹配到的大写字母是字符串的首字母,则直接返回小写字母
@@ -22,7 +22,7 @@ export function toFileName(str) {
* @returns {*}
*/
export function toClassName(str) {
- if (!str) return;
+ if (!str) return '';
return str
.split('-')
.map((v) => {
@@ -33,11 +33,11 @@ export function toClassName(str) {
}
/**
- * m转换成模型名
+ * 转换成模型名
* @param str
* @returns {*}
*/
export function toModelName(str) {
- if (!str) return;
- return str.replace('-', '_');
+ if (!str) return '';
+ return str.replaceAll('-', '_');
}
diff --git a/src/packages/command-parser/src/tokenizer.ts b/src/packages/command-parser/src/tokenizer.ts
index 43bd1916b477bb8ea7f61025f61f881396cb2aef..c7015c81ad33815403c090bf9e5a135224f59ec9 100644
--- a/src/packages/command-parser/src/tokenizer.ts
+++ b/src/packages/command-parser/src/tokenizer.ts
@@ -23,6 +23,21 @@ export type TTokenMethodResult = {
index: number;
};
+export const keyCommand = {
+ ai: {
+ command: 'ai:',
+ content: /^.$/,
+ },
+ search: {
+ command: 'ss:',
+ content: /^.$/,
+ },
+ alias: {
+ command: ':',
+ content: /^.$/,
+ },
+};
+
export class Tokenizer {
// 空格
// eslint-disable-next-line no-irregular-whitespace
@@ -33,16 +48,7 @@ export class Tokenizer {
readonly #lineR = /^\r$/;
readonly #lineN = /^\n$/;
// 关键词
- readonly #keyCommand = {
- ai: {
- command: 'ai:',
- content: /^[^()()|&]$/,
- },
- search: {
- command: 'ss:',
- content: /^[^()()|&]$/,
- },
- };
+ readonly #keyCommand = keyCommand;
// 圆括号字符
readonly #parenthesesLeft = /^[((]$/;
@@ -306,10 +312,26 @@ export class Tokenizer {
* @private
*/
#tokenizerKeywords(tokens: TToken[], input: string, index: number): TTokenMethodResult {
- const { ai, search } = this.#keyCommand;
+ const { ai, search, alias } = this.#keyCommand;
let char = input[index];
+ if (index === 0 && alias.command[0] === char) {
+ tokens.push({ type: ETokenType.keyCommand, value: alias.command });
+ index += alias.command.length;
+ char = input[index];
+ if (char) {
+ let content = '';
+ while (alias.content.test(char)) {
+ content += char;
+ char = input[++index];
+ }
+ if (content) {
+ tokens.push({ type: ETokenType.keyCommandContent, value: content.trim() });
+ }
+ }
+ return { isContinue: true, index };
+ }
// ai关键字匹配
- if (ai.command[0] === char) {
+ if (index === 0 && ai.command[0] === char) {
const command = input.substring(index, ai.command.length);
if (command === ai.command) {
tokens.push({ type: ETokenType.keyCommand, value: command });
@@ -330,7 +352,7 @@ export class Tokenizer {
}
// search关键字匹配
- if (search.command[0] === char) {
+ if (index === 0 && search.command[0] === char) {
const command = input.substring(index, search.command.length);
if (command === search.command) {
tokens.push({ type: ETokenType.keyCommand, value: command });
diff --git a/src/packages/editor/src/command-editor.ts b/src/packages/editor/src/command-editor.ts
index 59303ae301025aec7e1572808700fff7fd6c1e1a..3a72ac591c33ea45b12a9161ab6d35a36934f956 100644
--- a/src/packages/editor/src/command-editor.ts
+++ b/src/packages/editor/src/command-editor.ts
@@ -266,7 +266,6 @@ export class CommandEditor {
* @param str
*/
handleCommandInputAppend(str: string) {
- this.commandInput.focus();
let input = this.input;
let offsetText = this.getCursorOffsetText();
const isInputEl = this.#cursor.getOneRange().endContainer === this.#commandInput;
diff --git a/src/packages/iswork/src/cmdp/abstract-cmdp.ts b/src/packages/iswork/src/cmdp/abstract-cmdp.ts
index 0e3f6b1a0724e58f7517303d29d170e11e4328bb..c901176cc03c32c876682d2e14a3fc2024a144d4 100644
--- a/src/packages/iswork/src/cmdp/abstract-cmdp.ts
+++ b/src/packages/iswork/src/cmdp/abstract-cmdp.ts
@@ -64,7 +64,10 @@ export abstract class AbstractCmdp {
*/
protected initByAddress(address: string) {
if (!this.check(address)) {
- throw new ScopeError(`iswork.${this.constructor.name}`, `校验地址失败,请传入正确的"${this.protocol}"协议地址`);
+ throw new ScopeError(
+ `iswork.${this.constructor.name}`,
+ `校验地址失败,请传入正确的"${this.protocol}"协议地址,地址:${address}`
+ );
}
const info = this.parse(address);
this.address = address;
diff --git a/src/packages/iswork/src/orm/driver/indexedDB/indexedDB-runner.ts b/src/packages/iswork/src/orm/driver/indexedDB/indexedDB-runner.ts
index 0da498b7ff16613e9dbd8b7a9b6d1db00c23c5e3..de429c8d7572eba598eb7409c5ec581b726bf395 100644
--- a/src/packages/iswork/src/orm/driver/indexedDB/indexedDB-runner.ts
+++ b/src/packages/iswork/src/orm/driver/indexedDB/indexedDB-runner.ts
@@ -163,6 +163,9 @@ export class IndexedDBRunner extends AbstractRunner {
return data[field] <= value;
case 'in':
return isArray(value) && value.includes(data[field]);
+ case 'cont':
+ if (isString(value) && isString(data[field])) return data[field].includes(value);
+ return false;
default:
return false;
}
diff --git a/src/packages/iswork/src/orm/model/base-model.ts b/src/packages/iswork/src/orm/model/base-model.ts
index 3f60af6f9f51f3000d84d0ec38c4b8c7630324ee..b0d69affe529401e5b620901ba7b00da6ede9dec 100644
--- a/src/packages/iswork/src/orm/model/base-model.ts
+++ b/src/packages/iswork/src/orm/model/base-model.ts
@@ -129,4 +129,12 @@ export class BaseModel implements IBaseModel {
const rep = await this.getRepository();
return await rep.deleteById(this, id);
}
+
+ static async findOneById(
+ this: Model,
+ id: string | number
+ ): Promise>> {
+ const rep = await this.getRepository();
+ return (await rep.findOneById(this, id)) as TModelData>;
+ }
}
diff --git a/src/packages/iswork/src/orm/repository/repository.ts b/src/packages/iswork/src/orm/repository/repository.ts
index aedc9fc5fe1824bd98971464b40797090435297e..8d4630f172429c0b6eeccb88a8b744f912fda27b 100644
--- a/src/packages/iswork/src/orm/repository/repository.ts
+++ b/src/packages/iswork/src/orm/repository/repository.ts
@@ -47,4 +47,11 @@ export class Repository {
filter: ['id', 'eq', id],
});
}
+
+ async findOneById(model: TModelType, id: string | number): Promise {
+ const list = await this.#repositoryManager.query(model, {
+ filter: ['id', 'eq', id],
+ });
+ return list?.length ? list[0] : null;
+ }
}
diff --git a/src/packages/shell-ui/src/button/Button.svelte b/src/packages/shell-ui/src/button/Button.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c39591844701f69e331baec3689a99a7bc307700
--- /dev/null
+++ b/src/packages/shell-ui/src/button/Button.svelte
@@ -0,0 +1,88 @@
+
+
+
+
+
+
diff --git a/src/packages/shell-ui/src/button/index.ts b/src/packages/shell-ui/src/button/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e34d1bb8128032855f978068a0dd9bcbeaf66396
--- /dev/null
+++ b/src/packages/shell-ui/src/button/index.ts
@@ -0,0 +1,3 @@
+import Button from './Button.svelte';
+export const ShButton = Button;
+export default ShButton;
diff --git a/src/packages/shell-ui/src/formitem/FormItem.svelte b/src/packages/shell-ui/src/formitem/FormItem.svelte
index f4c0c44fc4749180bcc441273cf0c351ac1946ec..1eadee585aa361abba99ab020fbec27c4199d320 100644
--- a/src/packages/shell-ui/src/formitem/FormItem.svelte
+++ b/src/packages/shell-ui/src/formitem/FormItem.svelte
@@ -1,3 +1,15 @@
-
+
+
+
diff --git a/src/packages/shell-ui/src/index.ts b/src/packages/shell-ui/src/index.ts
index 4b18798c2c2a9e6f7393fee71132757e25205a8a..599f65250490e23217d4404b4e07b0ab549c71ca 100644
--- a/src/packages/shell-ui/src/index.ts
+++ b/src/packages/shell-ui/src/index.ts
@@ -14,3 +14,5 @@ export * from './select/index';
export * from './table/index';
export * from './text/index';
export * from './textarea/index';
+export * from './modal/index';
+export * from './button/index';
diff --git a/src/packages/shell-ui/src/input/Input.svelte b/src/packages/shell-ui/src/input/Input.svelte
index da5200456cee42fe950470f1791425a4a5f6e25d..c01c1a27faa3a6ab193378ed9bbd1444715b6131 100644
--- a/src/packages/shell-ui/src/input/Input.svelte
+++ b/src/packages/shell-ui/src/input/Input.svelte
@@ -4,7 +4,30 @@
export let attribute: Record = {};
-