Sfoglia il codice sorgente

Merge branch 'develop3.0' of http://106.12.23.251:10080/TEAMMODEL/TEAMModelOS into develop3.0

OnePsycho 5 anni fa
parent
commit
d41a8037e3
36 ha cambiato i file con 4019 aggiunte e 77 eliminazioni
  1. 617 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/AzureCosmosDBV3Repository.cs
  2. 46 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/AzureCosmosDBV3ServiceCollectionExtensions.cs
  3. 58 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/CosmosDBV3ClientSingleton.cs
  4. 45 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/CosmosDbQuery.cs
  5. 69 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/IAzureCosmosDBV3Repository.cs
  6. 11 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/ID.cs
  7. 139 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/PredicateExtensions.cs
  8. 819 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/SQLHelperParametric.cs
  9. 45 0
      TEAMModelOS.SDK/Module/AzureCosmosDBV3/SystemTextJsonCosmosSerializer.cs
  10. 1 0
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  11. BIN
      TEAMModelOS/ClientApp/src/assets/questionItem.png
  12. 80 0
      TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.less
  13. 77 0
      TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.vue
  14. 17 0
      TEAMModelOS/ClientApp/src/router/routes.js
  15. 4 2
      TEAMModelOS/ClientApp/src/store/module/schoolBaseInfo.js
  16. 80 53
      TEAMModelOS/ClientApp/src/view/Home.vue
  17. 5 5
      TEAMModelOS/ClientApp/src/view/coursemgmt/CourseClassroom.vue
  18. 1 0
      TEAMModelOS/ClientApp/src/view/coursemgmt/CourseManage.vue
  19. 0 2
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreateTest.css
  20. 47 0
      TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreate.less
  21. 266 0
      TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreate.vue
  22. 118 0
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateEvaluation.less
  23. 302 0
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateEvaluation.vue
  24. 42 0
      TEAMModelOS/ClientApp/src/view/learnactivity/ImportCreate.less
  25. 42 0
      TEAMModelOS/ClientApp/src/view/learnactivity/ImportCreate.vue
  26. 50 0
      TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.less
  27. 73 0
      TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.vue
  28. 0 0
      TEAMModelOS/ClientApp/src/view/learnactivity/StudentPreview.less
  29. 17 0
      TEAMModelOS/ClientApp/src/view/learnactivity/StudentPreview.vue
  30. 0 0
      TEAMModelOS/ClientApp/src/view/learnactivity/TeacherPreview.less
  31. 58 0
      TEAMModelOS/ClientApp/src/view/learnactivity/TeacherPreview.vue
  32. 184 0
      TEAMModelOS/ClientApp/src/view/selflearning/CreateLearnUnit.less
  33. 562 0
      TEAMModelOS/ClientApp/src/view/selflearning/CreateLearnUnit.vue
  34. 55 0
      TEAMModelOS/ClientApp/src/view/selflearning/CreateOrderLearn.less
  35. 61 0
      TEAMModelOS/ClientApp/src/view/selflearning/CreateOrderLearn.vue
  36. 28 15
      TEAMModelOS/Controllers/Analysis/AchievementController.cs

+ 617 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/AzureCosmosDBV3Repository.cs

@@ -0,0 +1,617 @@
+using Microsoft.Azure.Cosmos;
+using Microsoft.Azure.Cosmos.Linq;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Net;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Context.Attributes.Azure;
+using TEAMModelOS.SDK.Context.Exception;
+using TEAMModelOS.SDK.Helper.Common.ReflectorExtensions;
+using TEAMModelOS.SDK.Module.AzureCosmosDB.Configuration;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+    public class AzureCosmosDBV3Repository : IAzureCosmosDBV3Repository, IDisposable
+    {
+        private CosmosClient CosmosClient { get; set; }
+        /// <summary>
+        /// 线程安全的dict类型
+        /// </summary>
+        private Dictionary<string, Container> DocumentCollectionDict { get; set; } = new Dictionary<string, Container>();
+
+        private string DatabaseId { get; set; }
+        private int CollectionThroughput { get; set; }
+        private Database database = null;
+
+        private string[] ScanModel { get; set; }
+
+        public AzureCosmosDBV3Repository(AzureCosmosDBOptions options)
+        {
+            try
+            {
+                if (!string.IsNullOrEmpty(options.ConnectionString))
+                {
+                    CosmosClient = CosmosDBV3ClientSingleton.getInstance(options.ConnectionString, options.ConnectionKey).GetCosmosDBClient();
+
+                }
+                else
+                {
+                    throw new BizException("请设置正确的AzureCosmosDB数据库配置信息!");
+                }
+                DatabaseId = options.Database;
+                CollectionThroughput = options.CollectionThroughput;
+                ScanModel = options.ScanModel;
+                // InitializeDatabase().GetAwaiter().GetResult();
+            }
+            catch (CosmosException e)
+            {
+                Dispose(true);
+                throw new BizException(e.Message, 500, e.StackTrace);
+            }
+        }
+
+
+        public async Task InitializeDatabase()
+        {
+            try
+            {
+                database = await CosmosClient.CreateDatabaseIfNotExistsAsync(DatabaseId, CollectionThroughput);
+                FeedIterator<ContainerProperties> resultSetIterator = database.GetContainerQueryIterator<ContainerProperties>();
+                while (resultSetIterator.HasMoreResults)
+                {
+                    foreach (ContainerProperties container in await resultSetIterator.ReadNextAsync())
+                    {
+                        DocumentCollectionDict.TryAdd(container.Id, database.GetContainer(container.Id));
+                    }
+                }
+                //获取数据库所有的表
+                List<Type> types = ReflectorExtensions.GetAllTypeAsAttribute<CosmosDBAttribute>(ScanModel);
+                foreach (Type type in types)
+                {
+                    string PartitionKey = GetPartitionKey(type);
+                    string CollectionName = "";
+                    int RU = 0;
+                    IEnumerable<CosmosDBAttribute> attributes = type.GetCustomAttributes<CosmosDBAttribute>(true);
+                    if (!string.IsNullOrEmpty(attributes.First<CosmosDBAttribute>().Name))
+                    {
+                        CollectionName = attributes.First<CosmosDBAttribute>().Name;
+                    }
+                    else
+                    {
+                        CollectionName = type.Name;
+                    }
+                    if (attributes.First<CosmosDBAttribute>().RU > 400)
+                    {
+                        RU = attributes.First<CosmosDBAttribute>().RU;
+                    }
+                    else
+                    {
+                        RU = CollectionThroughput;
+                    }
+                    //如果表存在于数据则检查RU是否变动,如果不存在则执行创建DocumentCollection
+                    if (DocumentCollectionDict.TryGetValue(CollectionName, out Container collection))
+                    { //更新RU
+
+                        int? throughputResponse = await CosmosClient.GetDatabase(DatabaseId).GetContainer(collection.Id).ReadThroughputAsync();
+                        if (throughputResponse < RU)
+                        {
+                            await CosmosClient.GetDatabase(DatabaseId).GetContainer(collection.Id).ReplaceThroughputAsync(RU);
+                        }
+                    }
+                    else
+                    {
+                        ContainerProperties containerProperties = new ContainerProperties { Id = CollectionName };
+
+                        if (!string.IsNullOrEmpty(PartitionKey))
+                        {
+                            containerProperties.PartitionKeyPath = "/" + PartitionKey;
+                        }
+                        if (RU > CollectionThroughput)
+                        {
+                            CollectionThroughput = RU;
+                        }
+                        Container containerWithConsistentIndexing = await database.CreateContainerIfNotExistsAsync(containerProperties, throughput: CollectionThroughput);
+                        DocumentCollectionDict.TryAdd(CollectionName, containerWithConsistentIndexing);
+                    }
+                }
+            }
+            catch (CosmosException e)
+            {
+                throw new BizException(e.Message, 500, e.StackTrace);
+            }
+        }
+
+
+
+
+        private string GetPartitionKey<T>()
+        {
+            Type type = typeof(T);
+            return GetPartitionKey(type);
+        }
+        private string GetPartitionKey(Type type)
+        {
+            PropertyInfo[] properties = type.GetProperties();
+            List<PropertyInfo> attrProperties = new List<PropertyInfo>();
+            foreach (PropertyInfo property in properties)
+            {
+                if (property.Name.Equals("PartitionKey"))
+                {
+                    attrProperties.Add(property);
+                    break;
+                }
+                object[] attributes = property.GetCustomAttributes(true);
+                foreach (object attribute in attributes) //2.通过映射,找到成员属性上关联的特性类实例,
+                {
+                    if (attribute is PartitionKeyAttribute)
+                    {
+                        attrProperties.Add(property);
+                    }
+                }
+            }
+            if (attrProperties.Count <= 0)
+            {
+                throw new BizException(type.Name + "has no PartitionKey !");
+            }
+            else
+            {
+                if (attrProperties.Count == 1)
+                {
+                    return attrProperties[0].Name;
+                }
+                else { throw new BizException("PartitionKey can only be single!"); }
+            }
+        }
+
+
+        private async Task<Container> InitializeCollection<T>()
+        {
+            Type type = typeof(T);
+            string partitionKey = GetPartitionKey<T>();
+            string CollectionName;
+            IEnumerable<CosmosDBAttribute> attributes = type.GetCustomAttributes<CosmosDBAttribute>(true);
+            if (!string.IsNullOrEmpty(attributes.First<CosmosDBAttribute>().Name))
+            {
+                CollectionName = attributes.First<CosmosDBAttribute>().Name;
+            }
+            else
+            {
+                CollectionName = type.Name;
+            }
+            return await InitializeCollection(CollectionName, partitionKey);
+        }
+
+        private async Task<Container> InitializeCollection(string CollectionName, string PartitionKey)
+        {
+            /////内存中已经存在这个表则直接返回
+            if (DocumentCollectionDict.TryGetValue(CollectionName, out Container DocumentCollection))
+            {
+                return DocumentCollection;
+            }///如果没有则尝试默认创建
+            else
+            {
+                ContainerProperties containerProperties = new ContainerProperties { Id = CollectionName };
+                if (!string.IsNullOrEmpty(PartitionKey))
+                {
+                    containerProperties.PartitionKeyPath = "/" + PartitionKey;
+                }
+                Container containerWithConsistentIndexing = await database.CreateContainerIfNotExistsAsync(containerProperties, throughput: CollectionThroughput);
+                DocumentCollectionDict.TryAdd(CollectionName, containerWithConsistentIndexing);
+                return containerWithConsistentIndexing;
+            }
+        }
+
+
+        public async Task DeleteAll<T>(List<KeyValuePair<string, string>> ids) where T : ID
+        {
+            string partitionKey = GetPartitionKey<T>();
+            await Task.Run(() => Parallel.ForEach(ids, (item) =>
+            {
+                Task.WaitAll(DeleteAsync<T>(item.Value, item.Key));
+            }));
+        }
+
+        public async Task DeleteAll<T>(List<T> entities) where T : ID
+        {
+            string partitionKey = GetPartitionKey<T>();
+            Type type = typeof(T);
+            await Task.Run(() => Parallel.ForEach(entities, (item) =>
+            {
+                object o = type.GetProperty(partitionKey).GetValue(item, null);
+                Task.WaitAll(DeleteAsync<T>(item.id, o.ToString()));
+            }));
+        }
+        public async Task<T> DeleteAsync<T>(string id, string pk) where T : ID
+        {
+            Container container = await InitializeCollection<T>();
+            ItemResponse<T> response = await container.DeleteItemAsync<T>(id: id, partitionKey: new PartitionKey(pk));
+            return response.Resource;
+        }
+
+        public async Task<T> DeleteAsync<T>(T entity, string pk) where T : ID
+        {
+            Container container = await InitializeCollection<T>();
+            string partitionKey = GetPartitionKey<T>();
+            Type type = typeof(T);
+            object o = type.GetProperty(partitionKey).GetValue(entity, null);
+            ItemResponse<T> response = await container.DeleteItemAsync<T>(id: entity.id, partitionKey: new PartitionKey(o.ToString()));
+            return response.Resource;
+
+        }
+        //public async Task<T> DeleteAsync<T>(string id) where T : ID
+        //{
+        //    Container container = await InitializeCollection<T>();
+        //    ItemResponse<T> response = await container.DeleteItemAsync<T>(id: id, partitionKey: new PartitionKey(GetPartitionKey<T>()));
+        //    return response.Resource;
+        //}
+
+        public async Task<List<T>> FindAll<T>() where T : ID
+        {
+            Container container = await InitializeCollection<T>();
+            return await ResultsFromFeedIterator(container.GetItemQueryIterator<T>());
+
+        }
+        private async Task<List<T>> ResultsFromFeedIterator<T>(FeedIterator<T> query, int? maxItemCount = null)
+        {
+            List<T> results = new List<T>();
+            while (query.HasMoreResults)
+            {
+                foreach (T t in await query.ReadNextAsync())
+                {
+                    results.Add(t);
+                    if (results.Count == maxItemCount)
+                    {
+                        return results;
+                    }
+                }
+            }
+            return results;
+        }
+        private async Task<List<T>> ResultsFromFeedIterator<T>(FeedIterator<T> query, Func<List<T>, Task> batchAction, int itemsPerPage)
+        {
+            List<T> results = new List<T>();
+            while (query.HasMoreResults)
+            {
+                if (results.Count() >= itemsPerPage)
+                {
+                    await batchAction(results);
+                    results.Clear();
+                }
+
+                results.AddRange(await query.ReadNextAsync());
+            }
+
+            if (results.Count() > 0)
+            {
+                await batchAction(results);
+                results.Clear();
+            }
+
+            return results;
+        }
+        public async Task<List<dynamic>> FindByDict(string CollectionName, Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null)
+        {
+            if (DocumentCollectionDict.TryGetValue(CollectionName, out Container container))
+            {
+                //StringBuilder sql = new StringBuilder("select value(c) from c");
+                //SQLHelper.GetSQL(dict, ref sql);
+                //CosmosDbQuery cosmosDbQuery = new CosmosDbQuery
+                //{
+                //    QueryText = sql.ToString()
+
+                //};
+                StringBuilder sql = new StringBuilder("select value(c) from c");
+                CosmosDbQuery cosmosDbQuery = SQLHelperParametric.GetSQL(dict, sql);
+                QueryRequestOptions queryRequestOptions = GetDefaultQueryRequestOptions(itemsPerPage: GetEffectivePageSize(itemsPerPage, maxItemCount));
+                FeedIterator<dynamic> query = container.GetItemQueryIterator<dynamic>(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: queryRequestOptions);
+                return await ResultsFromFeedIterator(query, maxItemCount);
+            }
+            else
+            {
+                throw new BizException("CollectionName named:" + CollectionName + " dose not exsit in Database!");
+            }
+
+        }
+
+        public async Task<List<dynamic>> FindCountByDict(string CollectionName, Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null)
+        {
+            if (DocumentCollectionDict.TryGetValue(CollectionName, out Container container))
+            {
+                //StringBuilder sql = new StringBuilder("select  value count(c)  from c");
+                //SQLHelper.GetSQL(dict, ref sql);
+                //CosmosDbQuery cosmosDbQuery = new CosmosDbQuery
+                //{
+                //    QueryText = sql.ToString()
+
+                //};
+                StringBuilder sql = new StringBuilder("select  value count(c)  from c");
+                CosmosDbQuery cosmosDbQuery = SQLHelperParametric.GetSQL(dict, sql);
+                QueryRequestOptions queryRequestOptions = GetDefaultQueryRequestOptions(itemsPerPage: GetEffectivePageSize(itemsPerPage, maxItemCount));
+                FeedIterator<dynamic> query = container.GetItemQueryIterator<dynamic>(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: queryRequestOptions);
+                return await ResultsFromFeedIterator(query, maxItemCount);
+            }
+            else
+            {
+                throw new BizException("CollectionName named:" + CollectionName + " dose not exsit in Database!");
+            }
+        }
+
+        public async Task<List<T>> FindByParams<T>(Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null) where T : ID
+        {
+            return await FindByDict<T>(dict, itemsPerPage, maxItemCount, partitionKey);
+        }
+        public async Task<List<T>> FindByDict<T>(Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null) where T : ID
+        {
+
+            StringBuilder sql = new StringBuilder("select value(c) from c");
+            SQLHelper.GetSQL(dict, ref sql);
+            CosmosDbQuery cosmosDbQuery = new CosmosDbQuery
+            {
+                QueryText = sql.ToString()
+
+            };
+            QueryRequestOptions queryRequestOptions = GetDefaultQueryRequestOptions(itemsPerPage: GetEffectivePageSize(itemsPerPage, maxItemCount));
+            return await ResultsFromQueryAndOptions<T>(cosmosDbQuery, queryRequestOptions);
+        }
+
+        private async Task<List<T>> ResultsFromQueryAndOptions<T>(CosmosDbQuery cosmosDbQuery, QueryRequestOptions queryOptions, int? maxItemCount = null)
+        {
+            Container container = await InitializeCollection<T>();
+            FeedIterator<T> query = container.GetItemQueryIterator<T>(
+                queryDefinition: cosmosDbQuery.CosmosQueryDefinition,
+                requestOptions: queryOptions);
+
+            return await ResultsFromFeedIterator(query, maxItemCount);
+        }
+        private int GetEffectivePageSize(int itemsPerPage, int? maxItemCount)
+        {
+            return itemsPerPage == -1 ? maxItemCount ?? itemsPerPage : Math.Min(maxItemCount ?? itemsPerPage, itemsPerPage);
+        }
+        private QueryRequestOptions GetDefaultQueryRequestOptions(int? itemsPerPage = null,
+        int? maxBufferedItemCount = null,
+        int? maxConcurrency = null)
+        {
+            QueryRequestOptions queryRequestOptions = new QueryRequestOptions
+            {
+                MaxItemCount = itemsPerPage == -1 ? 1000 : itemsPerPage,
+                MaxBufferedItemCount = maxBufferedItemCount ?? 100,
+                MaxConcurrency = maxConcurrency ?? 50
+            };
+
+            return queryRequestOptions;
+        }
+        private async Task<List<T>> ResultsFromQueryAndOptions<T>(CosmosDbQuery cosmosDbQuery, Func<List<T>, Task> batchAction, QueryRequestOptions queryOptions)
+        {
+            Container container = await InitializeCollection<T>();
+            FeedIterator<T> query = container.GetItemQueryIterator<T>(
+                queryDefinition: cosmosDbQuery.CosmosQueryDefinition,
+                requestOptions: queryOptions);
+
+            return await ResultsFromFeedIterator(query, batchAction, queryOptions.MaxItemCount ?? 0);
+        }
+
+
+
+        private QueryRequestOptions GetQueryRequestOptions(int itemsPerPage)
+        {
+            QueryRequestOptions queryRequestOptions = new QueryRequestOptions
+            {
+                MaxItemCount = itemsPerPage
+            };
+
+            return queryRequestOptions;
+        }
+        public async Task<List<T>> FindLinq<T>(Expression<Func<T, bool>> query = null, Expression<Func<T, object>> order = null, bool isDesc = false, int itemsPerPage = -1, int? maxItemCount = null) where T : ID
+        {
+            //QueryRequestOptions queryRequestOptions = GetQueryRequestOptions(itemsPerPage);
+            QueryRequestOptions queryRequestOptions = GetDefaultQueryRequestOptions(itemsPerPage: GetEffectivePageSize(itemsPerPage, maxItemCount));
+            FeedIterator<T> feedIterator;
+            Container container = await InitializeCollection<T>();
+
+            if (query == null)
+            {
+                if (order != null)
+                {
+                    if (isDesc)
+                    {
+                        feedIterator = container
+                       .GetItemLinqQueryable<T>(requestOptions: queryRequestOptions).OrderByDescending(order)
+                       .ToFeedIterator();
+                    }
+                    else
+                    {
+                        feedIterator = container
+                       .GetItemLinqQueryable<T>(requestOptions: queryRequestOptions).OrderBy(order)
+                       .ToFeedIterator();
+                    }
+                }
+                else
+                {
+                    feedIterator = container
+                   .GetItemLinqQueryable<T>(requestOptions: queryRequestOptions)
+                   .ToFeedIterator();
+                }
+            }
+            else
+            {
+                if (order != null)
+                {
+                    if (isDesc)
+                    {
+                        feedIterator = container
+                        .GetItemLinqQueryable<T>(requestOptions: queryRequestOptions)
+                        .Where(query).OrderByDescending(order)
+                        .ToFeedIterator();
+                    }
+                    else
+                    {
+                        feedIterator = container
+                    .GetItemLinqQueryable<T>(requestOptions: queryRequestOptions)
+                    .Where(query).OrderBy(order)
+                    .ToFeedIterator();
+                    }
+                }
+                else
+                {
+                    feedIterator = container
+                    .GetItemLinqQueryable<T>(requestOptions: queryRequestOptions)
+                    .Where(query)
+                    .ToFeedIterator();
+                }
+            }
+            return await ResultsFromFeedIterator<T>(feedIterator);
+        }
+
+        public async Task<List<T>> FindSQL<T>(string sql, Dictionary<string, object> Parameters = null, int itemsPerPage = -1, int? maxItemCount = null) where T : ID
+        {
+            Container container = await InitializeCollection<T>();
+            QueryRequestOptions queryOptions = GetQueryRequestOptions(GetEffectivePageSize(itemsPerPage, maxItemCount));
+            if (Parameters != null)
+            {
+                CosmosDbQuery cosmosDbQuery = new CosmosDbQuery
+                {
+                    QueryText = sql,
+                    Parameters = Parameters
+                };
+                FeedIterator<T> feedIterator = container
+                .GetItemQueryIterator<T>(cosmosDbQuery.CosmosQueryDefinition, requestOptions: queryOptions);
+                return await ResultsFromFeedIterator(feedIterator);
+            }
+            else
+            {
+                QueryDefinition queryDefinition = new QueryDefinition(sql);
+                return await ResultsFromFeedIterator<T>(container.GetItemQueryIterator<T>(queryDefinition));
+            }
+        }
+
+        //public async Task<List<T>> FindSQL<T>(string sql, bool isPK) where T : ID
+        //{
+        //    Container container = await InitializeCollection<T>();
+        //    QueryDefinition queryDefinition = new QueryDefinition(sql);
+        //    return await ResultsFromFeedIterator<T>(container.GetItemQueryIterator<T>(queryDefinition));
+        //}
+
+        public async Task<T> ReplaceObject<T>(T entity) where T : ID
+        {
+            Container container = await InitializeCollection<T>();
+            ItemResponse<T> response = await container.ReplaceItemAsync(item: entity, id: entity.id);
+            if (response.StatusCode.Equals(HttpStatusCode.OK))
+            {
+                return response.Resource;
+            }
+            else { throw new BizException("error"); }
+
+        }
+
+        //public async Task<T> ReplaceObject<T>(T entity, string key, string partitionKey) where T : ID
+        //{
+        //    Container container = await InitializeCollection<T>();
+        //    ItemResponse<T> response = await container.ReplaceItemAsync(item: entity, id: entity.id);
+        //    if (response.StatusCode.Equals(HttpStatusCode.OK))
+        //    {
+        //        return response.Resource;
+        //    }
+        //    else { throw new BizException("error"); }
+
+        //}
+
+        public async Task<T> Save<T>(T entity) where T : ID
+        {
+            try
+            {
+                Container container = await InitializeCollection<T>();
+                ItemResponse<T> response = await container.CreateItemAsync<T>(entity);
+                return response.Resource;
+            }
+            catch (Exception e)
+            {
+                throw new BizException(e.Message);
+            }
+        }
+
+        public async Task<List<T>> SaveAll<T>(List<T> enyites) where T : ID
+        {
+
+            await Task.Run(() => Parallel.ForEach(enyites, (item) =>
+            {
+                Task.WaitAll(Save(item));
+            }));
+            return enyites;
+        }
+
+        public async Task<T> Update<T>(T entity) where T : ID
+        {
+            Container container = await InitializeCollection<T>();
+            ItemResponse<T> response = await container.UpsertItemAsync(entity);
+            return response.Resource;
+        }
+
+        public async Task<List<T>> UpdateAll<T>(List<T> entities) where T : ID
+        {
+            await Task.Run(() => Parallel.ForEach(entities, (item) =>
+            {
+                Task.WaitAll(Update(item));
+            }));
+            return entities;
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                CosmosClient?.Dispose();
+            }
+        }
+
+        public async Task<T> FindById<T>(string id) where T : ID
+        {
+            Container container = await InitializeCollection<T>();
+            CosmosDbQuery cosmosDbQuery = new CosmosDbQuery
+            {
+                QueryText = @"SELECT *
+                            FROM    c 
+                            WHERE   c.id = @id",
+                Parameters = new Dictionary<string, object>
+                 {
+
+                    { "@id",id}
+                }
+            };
+            FeedIterator<T> feedIterator = container
+               .GetItemQueryIterator<T>(cosmosDbQuery.CosmosQueryDefinition);
+            return (await ResultsFromFeedIterator(feedIterator)).SingleOrDefault();
+        }
+
+        public async Task<T> FindByIdPk<T>(string id, string pk) where T : ID
+        {
+            Container container = await InitializeCollection<T>();
+            ItemResponse<T> response = await container.ReadItemAsync<T>(id: id, partitionKey: new PartitionKey(pk));
+            return response.Resource;
+        }
+
+
+        public async Task<List<T>> FindByDictTest<T>(Dictionary<string, object> dict, int itemsPerPage = 1, int? maxItemCount = 1, string partitionKey = null) where T : ID
+        {
+            //Container container = await InitializeCollection<T>();
+            StringBuilder sql = new StringBuilder("select value(c) from c");
+            CosmosDbQuery cosmosDbQuery = SQLHelperParametric.GetSQL(dict, sql);
+            QueryRequestOptions queryRequestOptions = GetDefaultQueryRequestOptions(itemsPerPage: GetEffectivePageSize(itemsPerPage, maxItemCount));
+
+            //FeedIterator<T> feedIterator = container
+            //   .GetItemQueryIterator<T>(cosmosDbQuery.CosmosQueryDefinition);
+            // return (await ResultsFromFeedIterator(feedIterator)).SingleOrDefault();
+
+
+            return await ResultsFromQueryAndOptions<T>(cosmosDbQuery, queryRequestOptions, maxItemCount);
+        }
+    }
+}

+ 46 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/AzureCosmosDBV3ServiceCollectionExtensions.cs

@@ -0,0 +1,46 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using TEAMModelOS.SDK.Module.AzureCosmosDB.Configuration;
+using TEAMModelOS.SDK.Module.AzureCosmosDB.Interfaces;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+
+        public static class AzureCosmosDBServiceCollectionExtensions
+        {
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="services"></param>
+            /// <returns></returns>
+            private static AzureCosmosDBServiceBuilder AddCosmosDBServerBuilder(this IServiceCollection services)
+            {
+                return new AzureCosmosDBServiceBuilder(services);
+            }
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="services"></param>
+            /// <returns></returns>
+            public static AzureCosmosDBServiceBuilder AddAzureCosmosDBV3(this IServiceCollection services)
+            {
+                var builder = services.AddCosmosDBServerBuilder();
+                services.AddSingleton<IAzureCosmosDBV3Repository, AzureCosmosDBV3Repository>();
+                return builder;
+            }
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="builder"></param>
+            /// <param name="_connectionString"></param>
+            /// <returns></returns>
+            public static AzureCosmosDBServiceBuilder AddCosmosDBConnection(this AzureCosmosDBServiceBuilder builder, AzureCosmosDBOptions databaseOptions)
+            {
+                builder.Services.AddSingleton(databaseOptions);
+                return builder;
+            }
+        }
+}

+ 58 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/CosmosDBV3ClientSingleton.cs

@@ -0,0 +1,58 @@
+using Microsoft.Azure.Cosmos;
+using Microsoft.Azure.Cosmos.Fluent;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+    public class CosmosDBV3ClientSingleton
+    {
+        private static string _connectionUrl;
+        private static string _connectionKey;
+        private CosmosClient CosmosClient;
+
+        private CosmosDBV3ClientSingleton() { }
+
+
+
+        public CosmosClient GetCosmosDBClient()
+        {
+            if (CosmosClient != null)
+            {
+                return CosmosClient;
+            }
+            else
+            {
+                getInstance(_connectionUrl, _connectionKey);
+                return CosmosClient;
+            }
+        }
+
+        public static CosmosDBV3ClientSingleton getInstance(string connectionUrl, string connectionKey)
+        {
+            _connectionUrl = connectionUrl;
+            _connectionKey = connectionKey;
+            return SingletonInstance.instance;
+        }
+
+        private static class SingletonInstance
+        {
+            public static CosmosDBV3ClientSingleton instance = new CosmosDBV3ClientSingleton()
+            {
+                // CosmosClient    =new CosmosClient(_connectionUrl, _connectionKey, new CosmosClientOptions() { AllowBulkExecution = true } )
+                CosmosClient = new CosmosClientBuilder(_connectionUrl, _connectionKey).
+                WithBulkExecution(true).WithConnectionModeDirect().
+                //WithConnectionModeDirect().
+                WithCustomSerializer(new SystemTextJsonCosmosSerializer(new System.Text.Json.JsonSerializerOptions()))
+                .Build()
+                //CosmosClient = new CosmosClient(_connectionUrl, _connectionKey)
+            };
+            //private static readonly ConnectionPolicy ConnectionPolicy = new ConnectionPolicy
+            //{
+            //    ConnectionMode = ConnectionMode.Direct,
+            //    ConnectionProtocol = Protocol.Tcp
+            //};
+        }
+    }
+}

+ 45 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/CosmosDbQuery.cs

@@ -0,0 +1,45 @@
+using Microsoft.Azure.Cosmos;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+   public class CosmosDbQuery
+    {
+        public string QueryText { get; set; }
+
+        public Dictionary<string, object> Parameters { get; set; }
+
+        public QueryDefinition CosmosQueryDefinition
+        {
+            get
+            {
+                QueryDefinition queryDefinition = new QueryDefinition(QueryText);
+
+                if (Parameters != null)
+                {
+                    foreach (var parameter in Parameters)
+                    {
+                        queryDefinition = queryDefinition.WithParameter(parameter.Key, parameter.Value);
+                    }
+                }
+
+                return queryDefinition;
+            }
+        }
+
+        public CosmosDbQuery() { }
+
+        public CosmosDbQuery(string queryText)
+        {
+            QueryText = queryText;
+        }
+
+        public CosmosDbQuery(string queryText, Dictionary<string, object> parameters)
+        {
+            QueryText = queryText;
+            Parameters = parameters;
+        }
+    }
+}

+ 69 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/IAzureCosmosDBV3Repository.cs

@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+    public interface IAzureCosmosDBV3Repository
+    {
+
+        Task<T> Save<T>(T entity) where T : ID;
+        Task<List<T>> SaveAll<T>(List<T> enyites) where T : ID;
+        Task<T> DeleteAsync<T>(T entity, string pk) where T : ID;
+        Task<T> DeleteAsync<T>(string id, string pk) where T : ID;
+        Task DeleteAll<T>(List<T> entities) where T : ID;
+        Task DeleteAll<T>(List<KeyValuePair<string, string>> ids) where T : ID;
+        Task<T> Update<T>(T entity) where T : ID;
+        Task<List<T>> UpdateAll<T>(List<T> entities) where T : ID;
+        Task<T> ReplaceObject<T>(T entity) where T : ID;
+
+        Task<T> FindById<T>(string id) where T : ID;
+        Task<T> FindByIdPk<T>(string id, string pk) where T : ID;
+        // Task<string> ReplaceObject<T>(T entity, string key, string partitionKey) where T : ID;
+        Task<List<T>> FindAll<T>() where T : ID;
+        /// <summary>
+        /// QueryText = @"SELECT *
+        //            FROM    c 
+        //            WHERE   c.documentType = @documentType 
+        //                    AND c.id = @id",
+        //Parameters = new[]
+        //{
+        //    new CosmosDbQueryParameter("@documentType", GetDocumentType<T>()),
+        //    new CosmosDbQueryParameter("@id", id)
+        //}
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="sql"></param>
+        /// <param name="Parameters"></param>
+        /// <returns></returns>
+        Task<List<T>> FindSQL<T>(string sql, Dictionary<string, object> Parameters = null, int itemsPerPage = -1, int? maxItemCount = null) where T : ID;
+        //Task<List<T>> FindSQL<T>(string sql, bool isPK) where T : ID;
+
+        /// <summary>
+        /////正确的代码
+        //Expression<Func<QuestionFeed, bool>> predicate = null;
+        //      query = f =>  1==1;
+        //    query= query.And(x =>x.Address.City== "Seattle3");
+        //    query= query.Or(x => x.id== "Andersen.1");
+        //    query = query.And(x => x.Parents.Where(y => y.FirstName == "Thomas1").Any()) ;
+        //    Expression<Func<Family, object>> order = null;
+        //order = f => f.id;
+        //_questionFeedRepository.Entities.Where(predicate);
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="itemsPerPage"></param>
+        /// <param name="query"></param>
+        /// <returns></returns>
+        Task<List<T>> FindLinq<T>(Expression<Func<T, bool>> query = null, Expression<Func<T, object>> order = null, bool isDesc = false, int itemsPerPage = -1, int? maxItemCount = null) where T : ID;
+        Task<List<T>> FindByParams<T>(Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null) where T : ID;
+        Task<List<T>> FindByDict<T>(Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null) where T : ID;
+        Task<List<dynamic>> FindByDict(string CollectionName, Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null);
+        Task<List<dynamic>> FindCountByDict(string CollectionName, Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null);
+        Task InitializeDatabase();
+        Task<List<T>> FindByDictTest<T>(Dictionary<string, object> dict, int itemsPerPage = -1, int? maxItemCount = null, string partitionKey = null) where T : ID;
+
+
+    }
+}

+ 11 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/ID.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+    public interface ID
+    {
+        string id { get; set; }
+    }
+}

+ 139 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/PredicateExtensions.cs

@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+    /// <summary>
+    //
+    /*
+               Expression<Func<UserInfo, bool>> exp = x => x.Status == (int)BizConst.UserStatus.Active;  
+               if(parameters.IsAuthenticated)
+               {
+                   exp = exp.And(x => x.IdentityAuth == (int) BizConst.IdentityAuthStatus.Authed && x.CareerAuth == (int) BizConst.GlobalCareerStatus.Authed);
+               }
+               if(parameters.IsVip)
+               {
+                   exp = exp.And(x => x.Vip != (int) BizConst.VipTag.NoVip);
+               }*/
+    /// </summary>
+    public static class PredicateExtensions
+    {
+        ///// <summary>
+        ///// 机关函数应用True时:单个AND有效,多个AND有效;单个OR无效,多个OR无效;混应时写在AND后的OR有效。即,设置为True时所有or语句应该放在and语句之后,否则无效
+        ///// </summary>
+        //public static Expression<Func<T, bool>> True<T>() { return f => true; }
+
+        ///// <summary>
+        ///// 机关函数应用False时:单个AND无效,多个AND无效;单个OR有效,多个OR有效;混应时写在OR后面的AND有效。 即,设置为False时所有or语句应该放在and语句之前,否则无效
+        ///// </summary>
+        //public static Expression<Func<T, bool>> False<T>() { return f => false; }
+
+        //public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expression1,
+        //   Expression<Func<T, bool>> expression2)
+        //{
+        //    var invokedExpression = Expression.Invoke(expression2, expression1.Parameters
+        //            .Cast<Expression>());
+
+        //    return Expression.Lambda<Func<T, bool>>(Expression.Or(expression1.Body, invokedExpression),
+        //    expression1.Parameters);
+        //}
+
+        //public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1,
+        //      Expression<Func<T, bool>> expression2)
+        //{
+        //    var invokedExpression = Expression.Invoke(expression2, expression1.Parameters
+        //         .Cast<Expression>());
+
+        //    return Expression.Lambda<Func<T, bool>>(Expression.And(expression1.Body,
+        //           invokedExpression), expression1.Parameters);
+
+        //}
+
+
+        /// <summary>
+        /// 机关函数应用True时:单个AND有效,多个AND有效;单个OR无效,多个OR无效;混应时写在AND后的OR有效。即,设置为True时所有or语句应该放在and语句之后,否则无效
+        /// </summary>
+        public static Expression<Func<T, bool>> BaseAnd<T>() { return f => true; }
+        /// <summary>
+        /// 机关函数应用False时:单个AND无效,多个AND无效;单个OR有效,多个OR有效;混应时写在OR后面的AND有效。 即,设置为False时所有or语句应该放在and语句之前,否则无效
+        /// </summary>
+        public static Expression<Func<T, bool>> BaseOr<T>() { return f => false; }
+
+        public static Expression<Func<T, bool>> Or<T>(
+            this Expression<Func<T, bool>> expr1,
+            Expression<Func<T, bool>> expr2)
+        {
+            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
+            return Expression.Lambda<Func<T, bool>>
+                  (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
+        }
+
+        public static Expression<Func<T, bool>> And<T>(
+            this Expression<Func<T, bool>> expr1,
+            Expression<Func<T, bool>> expr2)
+        {
+            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
+            return Expression.Lambda<Func<T, bool>>
+                  (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
+        }
+
+        public static Expression Replace(this Expression expression,
+        Expression searchEx, Expression replaceEx)
+        {
+            return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
+        }
+
+        public static Expression<Func<T, bool>> CombineOrPreicatesWithAndPredicates<T>(this Expression<Func<T, bool>> combinedPredicate,
+            Expression<Func<T, bool>> andPredicate, Expression<Func<T, bool>> orPredicate)
+        {
+            combinedPredicate = combinedPredicate ?? BaseAnd<T>();
+            if (andPredicate != null && orPredicate != null)
+            {
+                andPredicate = andPredicate.And(orPredicate);
+                combinedPredicate = combinedPredicate.And(andPredicate);
+            }
+            else if (orPredicate != null)
+            {
+                combinedPredicate = combinedPredicate.And(orPredicate);
+            }
+            else
+            {
+                combinedPredicate = combinedPredicate.And(andPredicate);
+            }
+            return combinedPredicate;
+        }
+
+        public static void AddToPredicateTypeBasedOnIfAndOrOr<T>(ref Expression<Func<T, bool>> andPredicate,
+            ref Expression<Func<T, bool>> orPredicate, Expression<Func<T, bool>> newExpression, bool isAnd)
+        {
+            if (isAnd)
+            {
+                andPredicate = andPredicate ?? BaseAnd<T>();
+                andPredicate = andPredicate.And(newExpression);
+            }
+            else
+            {
+                orPredicate = orPredicate ?? BaseOr<T>();
+                orPredicate = orPredicate.Or(newExpression);
+            }
+        }
+    }
+    internal class ReplaceVisitor : ExpressionVisitor
+    {
+        private readonly Expression from, to;
+
+        public ReplaceVisitor(Expression from, Expression to)
+        {
+            this.from = from;
+            this.to = to;
+        }
+
+        public override Expression Visit(Expression node)
+        {
+            return node == from ? to : base.Visit(node);
+        }
+    }
+}

+ 819 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/SQLHelperParametric.cs

@@ -0,0 +1,819 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+    public class SQLHelperParametric
+    {
+        static readonly string[] LogicOpers = new string[] { " and ", " or " };
+        static readonly string[] CompareOpers = new string[] { " > ", " < ", " <= ", " >= ", " = ", " != ", " like ", " not like ", " in " };
+
+
+        public static void ReplaceKeyWords(ref StringBuilder sql)
+        {
+            sql.Replace(".order.", "['order'].");
+            sql.Replace(".order ", "['order'] ");
+
+            sql.Replace(".group.", "['group'].");
+            sql.Replace(".group ", "['group'] ");
+
+            sql.Replace(".end.", "['end'].");
+            sql.Replace(".end ", "['end'] ");
+
+            sql.Replace(".having.", "['having'].");
+            sql.Replace(".having ", "['having'] ");
+        }
+
+        public static CosmosDbQuery GetSQL(Dictionary<string, object> dict, StringBuilder sql)
+        {
+
+            if (dict != null)
+            {
+                Dictionary<string, object> parmeters = new Dictionary<string, object>();
+
+                int offsetNum = 0;
+                int limitNum = 0;
+                bool pageBool = false;
+                GetPageNum(dict, ref offsetNum, ref limitNum, ref pageBool);
+
+                //处理顺序
+                Stack<KeyValuePair<string, object>> stack = new Stack<KeyValuePair<string, object>>();
+
+                foreach (string item in dict.Keys)
+                {
+                    if (item.EndsWith(".|"))
+                    {
+                        stack.Push(new KeyValuePair<string, object>(item, dict[item]));
+                    }
+
+                }
+                foreach (string item in dict.Keys)
+                {
+                    if (!item.EndsWith(".|"))
+                    {
+                        stack.Push(new KeyValuePair<string, object>(item, dict[item]));
+                    }
+
+                }
+
+
+                string Join = " join ";
+                string instring = " in ";
+                Dictionary<string, string> keyValues = new Dictionary<string, string>();
+                StringBuilder WhereString = new StringBuilder();
+                int heada = 0;
+                string[] sqlHead = new string[] { "A", "B", "C", "D", "E", "F" };
+                int kslength = 0;
+                int logicOperNum = 0;
+                bool keyListValueList = true;
+                //string distinctHead = "select distinct value(c) from c ";
+
+
+                int stackCount = stack.Count;
+                //foreach (KeyValuePair<string, object> item in newDict)
+                for (int k = 0; k < stackCount; k++)
+                {
+                    KeyValuePair<string, object> item = stack.Pop();
+                    bool isLikeSQL = false;
+                    if (item.Key.StartsWith("$.") || item.Key.StartsWith("!$."))
+                    {
+                        isLikeSQL = true;
+                    }
+
+
+                    string key = item.Key;
+                    string[] keyHead = key.Split(".");
+                    int index = 0;
+                    int compareOper = 4;
+                    int logicOper = 0;
+
+                    if (key.EndsWith(".&"))
+                    {
+                        logicOper = (int)LogicOper.and;
+                        key = key.Replace(".&", "");
+                    }
+                    else if (key.EndsWith(".|"))
+                    {
+                        logicOper = (int)LogicOper.or;
+                        key = key.Replace(".|", "");
+                    }
+                    CompareOperSwitch(keyHead[0], ref key, ref compareOper);
+                    string[] keyBody = key.Split("[*]");
+                    if (keyBody.Length > 1)
+                    {
+                        key = key.Replace("[*].", "");
+                        key = key.Replace(".", "");
+
+                        kslength += keyBody.Length;
+                        if (kslength < (7 + heada))
+                        {
+                            StringBuilder sqlitem = new StringBuilder();
+                            for (int i = 0; i < keyBody.Length - 1; i++)
+                            {
+                                //Console.WriteLine(ks[i]);
+                                if (i == 0)
+                                {
+                                    sqlitem.Append(Join);
+                                    string a = sqlHead[heada] + index;
+                                    sqlitem.Append(a + " ");
+                                    //keyValues.Add(ks[i], a);
+                                    keyValues[keyBody[i]] = a;
+                                    sqlitem.Append(instring);
+                                    sqlitem.Append("c.");
+                                    sqlitem.Append(keyBody[i]);
+                                }
+                                else
+                                {
+                                    sqlitem.Append(Join);
+                                    string a = sqlHead[heada] + index;
+                                    sqlitem.Append(a + " ");
+                                    //keyValues.Add(ks[i], a);
+                                    keyValues[keyBody[i]] = a;
+                                    sqlitem.Append(instring);
+                                    sqlitem.Append(keyValues[keyBody[i - 1]]);
+                                    sqlitem.Append(keyBody[i]);
+                                }
+                                index += 1;
+                            }
+                            sql.Append(sqlitem);
+                            string s = "";
+                            if (isLikeSQL)
+                            {
+                                if (item.Value is JArray array)
+                                {
+                                    s = ValueIsLike(sqlHead[heada] + (keyBody.Length - 2) + keyBody[index] + "", key, array, LogicOpers[logicOper], logicOperNum, compareOper, ref keyListValueList);
+                                }
+                                else if (item.Value is IEnumerable enumerable && !(item.Value is String))
+                                {
+                                    s = ValueIsLike(sqlHead[heada] + (keyBody.Length - 2) + keyBody[index] + "", key, enumerable, LogicOpers[logicOper], logicOperNum, compareOper, ref keyListValueList);
+                                }
+                                else if (item.Value is JsonElement jsonElement1)
+                                {
+                                    if (jsonElement1.ValueKind is JsonValueKind.Object)
+                                    {
+
+                                        string compareOperBool = " true ";
+                                        compareOperBool = CompareBoolSwitch(compareOper);
+                                        string logicOperString = " and ";
+                                        if (logicOperNum != 0) logicOperString = LogicOpers[logicOper];
+                                        s = logicOperString + "Contains(" + sqlHead[heada] + (keyBody.Length - 2) + keyBody[index] + " , \'" + item.Value.ToString() + "\') = " + compareOperBool + " ";
+                                    }
+                                    else
+                                    {
+                                        s = ValueIsLike(sqlHead[heada] + (keyBody.Length - 2) + keyBody[index] + "", key, jsonElement1, LogicOpers[logicOper], logicOperNum, compareOper, ref keyListValueList);
+                                    }
+
+                                }
+                                else
+                                {
+                                    s = ValueIsLike(sqlHead[heada] + (keyBody.Length - 2) + keyBody[index] + "", key, item.Value, LogicOpers[logicOper], logicOperNum, compareOper, ref keyListValueList);
+                                }
+                            }
+                            else
+                            {
+                                s = ValueNotLike(sqlHead[heada] + (keyBody.Length - 2) + keyBody[index] + "", key, item.Value, LogicOpers[logicOper], logicOperNum, compareOper, ref keyListValueList);
+                            }
+                            WhereString.Append(s);
+
+                            if (keyListValueList)
+                            {
+                                sql = sql.Replace("select ", "select distinct ");
+                            }
+                        }
+                        else
+                        {
+                            //throw new BizException("数组总共深度不能超过5层", ResponseCode.PARAMS_ERROR);
+                        }
+                        heada += 1;
+
+                    }
+                    else
+                    {
+
+                        string itemKey = item.Key.Replace(".", "");
+                        WhereString.Append(KeyNotElement(dict[item.Key], item.Key, itemKey, LogicOpers[logicOper], logicOperNum, compareOper));
+
+                    }
+                    // heada += 1;
+                    logicOperNum += 1;
+                }
+                sql.Append(" where 1=1 ").Append(WhereString);
+                if (pageBool)
+                {
+                    sql.Append(" OFFSET " + offsetNum + " LIMIT " + limitNum);
+                }
+
+                ReplaceKeyWords(ref sql);
+                //sql = sql.Replace("[*].", "");
+
+                parmeters = GetParmeter(dict, parmeters);
+
+                CosmosDbQuery cosmosDbQuery = new CosmosDbQuery
+                {
+                    QueryText = sql.ToString(),
+                    Parameters = parmeters
+                };
+
+                return cosmosDbQuery;
+            }
+
+            return null;
+        }
+
+        private static void GetPageNum(Dictionary<string, object> dict, ref int offsetNum, ref int limitNum, ref bool pageBool)
+        {
+            dict.TryGetValue("OFFSET", out object offset);
+            dict.Remove("OFFSET");
+            dict.TryGetValue("LIMIT", out object limit);
+            dict.Remove("LIMIT");
+            if (offset != null && limit != null)
+            {
+                pageBool = true;
+                offsetNum = int.Parse(offset.ToString());
+                limitNum = int.Parse(limit.ToString());
+            }
+        }
+
+        private static void CompareOperSwitch(string keyHead, ref string key, ref int compareOper)
+        {
+            switch (keyHead)
+            {
+                case ">":
+                    compareOper = (int)CompareOper.moreThan;
+                    key = key.Replace(">.", "");
+                    break;
+                case "<":
+                    compareOper = (int)CompareOper.lessThan;
+                    key = key.Replace("<.", "");
+                    break;
+                case "<=":
+                    compareOper = (int)CompareOper.notMoreThan;
+                    key = key.Replace("<=.", "");
+                    break;
+                case ">=":
+                    compareOper = (int)CompareOper.notLessThan;
+                    key = key.Replace(">=.", "");
+                    break;
+                case "=":
+                    compareOper = (int)CompareOper.equal;
+                    key = key.Replace("=.", "");
+                    break;
+                case "!=":
+                    compareOper = (int)CompareOper.notEqual;
+                    key = key.Replace("!=.", "");
+                    break;
+                case "$":
+                    compareOper = (int)CompareOper.like;
+                    key = key.Replace("$.", "");
+                    break;
+                case "!$":
+                    compareOper = (int)CompareOper.notLike;
+                    key = key.Replace("!$.", "");
+                    break;
+                default:
+                    compareOper = 4;
+                    break;
+            }
+        }
+
+        private static string ValueNotLike(string key, string key1, object value, string logicOperParams, int logicOperNum, int compareOperNum, ref bool keyListValueList)
+        {
+
+            string logicOper = " and ";
+            string compareOper = " = ";
+            if (compareOperNum != 4) compareOper = CompareOpers[compareOperNum];
+            if (logicOperNum != 0) logicOper = logicOperParams;
+            StringBuilder sql = new StringBuilder(logicOper + key + " in (");
+            if (value is JArray array)
+            {
+                int aa = 0;
+                foreach (JValue obja in array)
+                {
+                    sql.Append(" @" + key1 + aa + " ,");
+                    //if (obja.Value is string a)
+                    //{
+                    //    sql.Append("\'" + a + "\',");
+                    //}
+                    //if (obja.Value is int b)
+                    //{
+                    //    sql.Append(b + ",");
+                    //}
+                    //if (obja.Value is double c)
+                    //{
+                    //    sql.Append(c + ",");
+                    //}
+                    //if (obja.Value is bool d)
+                    //{
+                    //    sql.Append(d + ",");
+                    //}
+                    //if (obja.Value is long e)
+                    //{
+                    //    sql.Append(e + ",");
+                    //}
+                    //if (obja.Value is DateTime f)
+                    //{
+                    //    sql.Append(f + ",");
+                    //}
+                    aa++;
+                }
+                string sqls = sql.ToString().Substring(0, sql.Length - 1);
+                sqls += " ) ";
+                return sqls;
+            }
+            else if (value is IEnumerable enumerable && !(value is String))
+            {
+                int aa = 0;
+                foreach (object obja in enumerable)
+                {
+                    sql.Append(" @" + key1 + aa + " ,");
+                    //if (obja is string a)
+                    //{
+                    //    sql.Append("\'" + a + "\',");
+                    //}
+                    //if (obja is int b)
+                    //{
+                    //    sql.Append(" " + b + " ,");
+
+                    //}
+                    //if (obja is double c)
+                    //{
+                    //    sql.Append(" " + c + " ,");
+                    //}
+                    //if (obja is bool d)
+                    //{
+                    //    sql.Append(" " + d + " ,");
+
+                    //}
+                    //if (obja is long e)
+                    //{
+                    //    sql.Append(" " + e + " ,");
+                    //}
+                    //if (obja is DateTime f)
+                    //{
+                    //    sql.Append(" " + f + " ,");
+                    //}
+                    aa++;
+                }
+                string sqls = sql.ToString().Substring(0, sql.Length - 1);
+                sqls += ") ";
+                return sqls;
+            }
+            else if (value is JsonElement jsonElement)
+            {
+                if (jsonElement.ValueKind is JsonValueKind.Array)
+                {
+                    int aa = 0;
+                    foreach (JsonElement obja in jsonElement.EnumerateArray().ToArray())
+                    {
+                        sql.Append(" @" + key1 + aa + " ,");
+
+                        //if (obja.ValueKind is JsonValueKind.String)
+                        //{
+                        //    sql.Append("\'" + obja.ToString() + "\',");
+                        //}
+                        //if (obja.ValueKind is JsonValueKind.Number)
+                        //{
+                        //    sql.Append(" " + int.Parse(obja.ToString()) + " ,");
+                        //}
+                        //if (obja.ValueKind is JsonValueKind.True)
+                        //{
+                        //    sql.Append(" " + bool.Parse(obja.ToString()) + " ,");
+                        //}
+                        //if (obja.ValueKind is JsonValueKind.False)
+                        //{
+                        //    sql.Append(" " + bool.Parse(obja.ToString()) + " ,");
+                        //}
+                        aa++;
+                    }
+                }
+                else
+                {
+                    return logicOper + key + compareOper + " @" + key1 + " ";
+
+                    //if (jsonElement.ValueKind is JsonValueKind.String)
+                    //{
+                    //    return logicOper + key + compareOper + "\'" + value.ToString() + "\'";
+                    //}
+                    //if (jsonElement.ValueKind is JsonValueKind.Number)
+                    //{
+                    //    return logicOper + key + compareOper + double.Parse(value.ToString());
+                    //}
+                    //if (jsonElement.ValueKind is JsonValueKind.True)
+                    //{
+                    //    return logicOper + key + compareOper + bool.Parse(value.ToString());
+                    //}
+                    //if (jsonElement.ValueKind is JsonValueKind.False)
+                    //{
+                    //    return logicOper + key + compareOper + bool.Parse(value.ToString());
+                    //}
+
+                }
+
+                string sqls = sql.ToString().Substring(0, sql.Length - 1);
+                sqls += " ) ";
+                return sqls;
+            }
+            else
+            {
+                Type s = value.GetType();
+                TypeCode typeCode = Type.GetTypeCode(s);
+                if (compareOperNum == 4) keyListValueList = false;
+                return logicOper + key + compareOper + " @" + key1 + " ";
+                //return typeCode switch
+                //{
+                //    TypeCode.String => logicOper + key + compareOper + "\'" + value.ToString() + "\'",
+                //    TypeCode.Char => logicOper + key + compareOper + "\'" + value.ToString() + "\'",
+                //    TypeCode.Int32 => logicOper + key + compareOper + int.Parse(value.ToString()),
+                //    TypeCode.Double => logicOper + key + compareOper + double.Parse(value.ToString()),
+                //    //case TypeCode.Byte: return "and c." + key + "=" + (Byte)obj ;   
+                //    TypeCode.Boolean => logicOper + key + compareOper + bool.Parse(value.ToString()),
+                //    TypeCode.DateTime => logicOper + key + compareOper + (DateTime)value,
+                //    TypeCode.Int64 => logicOper + key + compareOper + long.Parse(value.ToString()),
+                //    _ => null,
+                //};
+            }
+        }
+
+        private static string ValueIsLike(string key, string key1, object value, string logicOperParams, int logicOperNum, int compareOperNum, ref bool keyListValueList)
+        {
+            string compareOperBool = " true ";
+            compareOperBool = CompareBoolSwitch(compareOperNum);
+            string logicOper = " and ";
+            if (logicOperNum != 0) logicOper = logicOperParams;
+            StringBuilder s = new StringBuilder(logicOper + " ( Contains( ");
+
+            if (value is JArray array)
+            {
+                int aa = 0;
+                foreach (JValue obja in array)
+                {
+                    if (aa != 0) s.Append("or Contains(");
+                    s.Append(key + "," + " @" + key1 + aa + " ) = " + compareOperBool + " ");
+
+                    //if (obja.Value is string a)
+                    //{
+                    //    s.Append(key + "," + "\'" + a + "\') = " + compareOperBool + " ");
+                    //}
+                    //else if (obja.Value is int b)
+                    //{
+                    //    s.Append("ToString( " + key + " )," + " \'" + b + "\' ) = " + compareOperBool + " ");
+
+                    //}
+                    //else if (obja.Value is double c)
+                    //{
+                    //    s.Append("ToString( " + key + " )," + c + "\' ) = " + compareOperBool + " ");
+                    //}
+                    //else if (obja.Value is bool d)
+                    //{
+                    //    s.Append("ToString( " + key + " )," + "\' " + d + "\' ) = " + compareOperBool + " ");
+
+                    //}
+                    //else if (obja.Value is long e)
+                    //{
+                    //    s.Append("ToString( " + key + " )," + " \'" + e + "\' ) = " + compareOperBool + " ");
+                    //}
+                    //else if (obja.Value is DateTime f)
+                    //{
+                    //    s.Append("ToString( " + key + " )," + " \'" + f + "\' ) = " + compareOperBool + " ");
+                    //}
+                    aa++;
+                }
+            }
+            else if (value is IEnumerable enumerable && !(value is String))
+            {
+                int aa = 0;
+                foreach (object obja in enumerable)
+                {
+                    if (aa != 0) s.Append("or Contains(");
+                    s.Append(key + "," + " @" + key1 + aa + " ) = " + compareOperBool + " ");
+                    aa++;
+                }
+
+            }
+            else if (value is JsonValueKind.Array && value is JsonElement jsonElement)
+            {
+                int aa = 0;
+                //jsonElement.EnumerateArray().ToArray();
+                foreach (JsonElement obja in jsonElement.EnumerateArray().ToArray())
+                {
+                    if (aa != 0) s.Append("or Contains(");
+                    s.Append(key + "," + " @" + key1 + aa + " ) = " + compareOperBool + " ");
+
+
+                    aa++;
+                }
+            }
+            else
+            {
+                Type stype = value.GetType();
+                TypeCode typeCode = Type.GetTypeCode(stype);
+                keyListValueList = false;
+                string sql = "";
+
+                sql = logicOper + "Contains( " + key + " ,  @" + key1 + " ) = " + compareOperBool + " ";
+                return sql;
+            }
+            s.Append(" )");
+            return s.ToString();
+        }
+
+        private static string CompareBoolSwitch(int compareOperNum)
+        {
+            return compareOperNum switch
+            {
+                6 => " true ",
+                7 => " false ",
+                _ => " true ",
+            };
+
+        }
+
+        private static string KeyNotElement(object value, string key, string key1, string logicOperParams, int logicOperNum, int compareOperNum)
+        {
+            string compareOperBool = " true ";
+            compareOperBool = CompareBoolSwitch(compareOperNum);
+            string logicOper = " and ";
+            int compareOper = 4;
+            if (logicOperNum != 0) logicOper = logicOperParams;
+            if (key.EndsWith(".&"))
+            {
+                key = key.Replace(".&", "");
+            }
+            else if (key.EndsWith(".|"))
+            {
+                key = key.Replace(".|", "");
+            }
+            string[] keyHead = key.Split(".");
+            CompareOperSwitch(keyHead[0], ref key, ref compareOper);
+
+            if (compareOper == 6 || compareOper == 7)
+            {
+                StringBuilder sql = new StringBuilder(logicOper + " ( Contains( ");
+                if (value is JArray jarray)
+                {
+                    int aa = 0;
+                    foreach (JValue obja in jarray)
+                    {
+                        if (aa != 0) sql.Append("or Contains(");
+                        sql.Append(" c." + key + ", @" + key1 + aa + " )= " + compareOperBool + "  ");
+
+
+                        aa++;
+                    }
+                }
+                else if (value is IEnumerable enumerable && !(value is String))
+                {
+                    int aa = 0;
+                    foreach (object obja in enumerable)
+                    {
+                        if (aa != 0) sql.Append("or Contains(");
+                        sql.Append(" c." + key + "," + " @" + key1 + aa + " ) = " + compareOperBool + "  ");
+
+                        aa++;
+                    }
+                }
+                else if (value is JsonElement jsonElement && jsonElement.ValueKind! is JsonValueKind.Array)
+                {
+                    int aa = 0;
+                    foreach (JsonElement obja in jsonElement.EnumerateArray().ToArray())
+                    {
+                        if (aa != 0) sql.Append("or Contains(");
+                        sql.Append(" c." + key + "," + " @" + key1 + aa + " ) = " + compareOperBool + "  ");
+
+                        aa++;
+                    }
+                }
+                else
+                {
+                    Type s = value.GetType();
+                    TypeCode typeCode = Type.GetTypeCode(s);
+
+                    string sql1 = "";
+                    sql1 = logicOper + "Contains(  c." + key + " ,  @" + key1 + " ) = " + compareOperBool + " ";
+
+
+                    return sql1;
+                }
+
+                sql.Append(")");
+                return sql.ToString();
+            }
+            else
+            {
+                StringBuilder sql = new StringBuilder(logicOper + " c." + key + " in (");
+                if (value is JArray array)
+                {
+                    int aa = 0;
+                    foreach (JValue obja in array)
+                    {
+
+                        sql.Append(" @" + key1 + aa + " ,");
+
+
+                        aa++;
+                    }
+                    string sqls = sql.ToString().Substring(0, sql.Length - 1);
+                    sqls += " ) ";
+                    return sqls;
+                }
+                else if (value is IEnumerable enumerable && !(value is String))
+                {
+                    int aa = 0;
+                    foreach (object obja in enumerable)
+                    {
+                        sql.Append(" @" + key1 + aa + " ,");
+
+                        aa++;
+                    }
+                    string sqls = sql.ToString().Substring(0, sql.Length - 1);
+                    sqls += " ) ";
+                    return sqls;
+                }
+                else if (value is JsonElement jsonElement)
+                {
+                    if (jsonElement.ValueKind is JsonValueKind.Array)
+                    {
+                        int aa = 0;
+                        foreach (JsonElement obja in jsonElement.EnumerateArray().ToArray())
+                        {
+                            sql.Append(" @" + key1 + aa + " ,");
+
+                            aa++;
+                        }
+                    }
+                    else
+                    {
+                        return logicOper + " c." + key + CompareOpers[compareOperNum] + " @" + key1;
+
+                    }
+
+                    string sqls = sql.ToString().Substring(0, sql.Length - 1);
+                    sqls += " ) ";
+                    return sqls;
+                }
+                else
+                {
+                    Type s = value.GetType();
+                    TypeCode typeCode = Type.GetTypeCode(s);
+
+                    return typeCode switch
+                    {
+                        TypeCode.String => logicOper + " c." + key + CompareOpers[compareOperNum] + " @" + key1,// + "\'" + value.ToString() + "\'",
+                        TypeCode.Char => logicOper + " c." + key + CompareOpers[compareOperNum] + " @" + key1,//  + "\'"  + value.ToString() + "\'",
+                        TypeCode.Int32 => logicOper + "  c." + key + CompareOpers[compareOperNum] + " @" + key1,// + int.Parse(value.ToString()),
+                        TypeCode.Double => logicOper + "  c." + key + CompareOpers[compareOperNum] + " @" + key1,// + double.Parse(value.ToString()),
+                        //case TypeCode.Byte: return "and c." + key + "=" + (Byte)obj ;   
+                        TypeCode.Boolean => logicOper + "  c." + key + CompareOpers[compareOperNum] + " @" + key1,//  + bool.Parse(value.ToString()),
+                        TypeCode.DateTime => logicOper + "  c." + key + CompareOpers[compareOperNum] + " @" + key1,//  + (DateTime)value,
+                        TypeCode.Int64 => logicOper + "  c." + key + CompareOpers[compareOperNum] + " @" + key1,// + long.Parse(value.ToString()),
+                        _ => null,
+                    };
+                }
+            }
+        }
+
+
+        public enum LogicOper : int
+        {
+            and = 0, or = 1
+        }
+        public enum CompareOper : int
+        {
+            moreThan = 0, lessThan = 1, notMoreThan = 2, notLessThan = 3, equal = 4, notEqual = 5, like = 6, notLike = 7, IN = 8
+        }
+
+
+
+
+        private static Dictionary<string, object> GetParmeter(Dictionary<string, object> dict, Dictionary<string, object> parmeters)
+        {
+            foreach (KeyValuePair<string, object> keyValue in dict)
+            {
+                string key = "";
+                string[] keyHead = keyValue.Key.Split(".");
+                switch (keyHead[0])
+                {
+                    case ">":
+                        key = keyValue.Key.Replace(">.", "");
+                        break;
+                    case "<":
+                        key = keyValue.Key.Replace("<.", "");
+                        break;
+                    case "<=":
+                        key = keyValue.Key.Replace("<=.", "");
+                        break;
+                    case ">=":
+                        key = keyValue.Key.Replace(">=.", "");
+                        break;
+                    case "=":
+                        key = keyValue.Key.Replace("=.", "");
+                        break;
+                    case "!=":
+                        key = keyValue.Key.Replace("!=.", "");
+                        break;
+                    case "$":
+                        key = keyValue.Key.Replace("$.", "");
+                        break;
+                    case "!$":
+                        key = keyValue.Key.Replace("!$.", "");
+                        break;
+                    default:
+                        key = keyValue.Key;
+                        break;
+                }
+                if (key.EndsWith(".&"))
+                {
+                    key = key.Replace(".&", "");
+                }
+                else if (key.EndsWith(".|"))
+                {
+                    key = key.Replace(".|", "");
+                }
+                key = key.Replace("[*].", "");
+                key = key.Replace(".", "");
+                if (keyValue.Value is JArray array)
+                {
+                    int aa = 0;
+                    foreach (JValue obja in array)
+                    {
+                        parmeters.Add("@" + key + aa, obja);
+                        aa++;
+                    }
+                }
+                else if (keyValue.Value is JsonElement jsonElement)
+                {
+                    if (jsonElement.ValueKind is JsonValueKind.Array)
+                    {
+                        int aa = 0;
+                        foreach (JsonElement obja in jsonElement.EnumerateArray().ToArray())
+                        {
+                            if (obja.ValueKind is JsonValueKind.String)
+                            {
+                                parmeters.Add("@" + key + aa, obja.ToString());
+                            }
+                            if (obja.ValueKind is JsonValueKind.Number)
+                            {
+                                parmeters.Add("@" + key + aa, double.Parse(obja.ToString()));
+                            }
+                            if (obja.ValueKind is JsonValueKind.True)
+                            {
+                                parmeters.Add("@" + key + aa, bool.Parse(obja.ToString()));
+                            }
+                            if (obja.ValueKind is JsonValueKind.False)
+                            {
+                                parmeters.Add("@" + key + aa, bool.Parse(obja.ToString()));
+                            }
+                            aa++;
+                        }
+                    }
+                    else
+                    {
+                        if (jsonElement.ValueKind is JsonValueKind.String)
+                        {
+                            parmeters.Add("@" + key, keyValue.Value.ToString());
+                        }
+                        else if (jsonElement.ValueKind is JsonValueKind.Number)
+                        {
+                            parmeters.Add("@" + key, double.Parse(keyValue.Value.ToString()));
+                        }
+                        else if (jsonElement.ValueKind is JsonValueKind.True)
+                        {
+                            parmeters.Add("@" + key, bool.Parse(keyValue.Value.ToString()));
+                        }
+                        else if (jsonElement.ValueKind is JsonValueKind.False)
+                        {
+                            parmeters.Add("@" + key, bool.Parse(keyValue.Value.ToString()));
+                        }
+                        else
+                        {
+                            parmeters.Add("@" + key, keyValue.Value.ToString());
+                        }
+                    }
+                }
+                else if (keyValue.Value is IEnumerable enumerable)
+                {
+                    int aa = 0;
+                    foreach (object obja in enumerable)
+                    {
+                        parmeters.Add("@" + key + aa, obja);
+                        aa++;
+                    }
+                }
+                else
+                {
+                    parmeters.Add("@" + key, keyValue.Value);
+                }
+                //parmeters.Add("@" + key, keyValue.Value.ToString());
+            }
+            return parmeters;
+        }
+
+
+    }
+}

+ 45 - 0
TEAMModelOS.SDK/Module/AzureCosmosDBV3/SystemTextJsonCosmosSerializer.cs

@@ -0,0 +1,45 @@
+using Microsoft.Azure.Cosmos;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Module.AzureCosmosDBV3
+{
+    internal sealed class SystemTextJsonCosmosSerializer : CosmosSerializer
+    {
+        private readonly JsonSerializerOptions _options;
+
+        internal SystemTextJsonCosmosSerializer(JsonSerializerOptions options)
+        {
+            _options = options;
+        }
+
+        /// <inheritdoc />
+        public override T FromStream<T>(Stream stream)
+        {
+            // Have to dispose of the stream, otherwise the Cosmos SDK throws.
+            // https://github.com/Azure/azure-cosmos-dotnet-v3/blob/0843cae3c252dd49aa8e392623d7eaaed7eb712b/Microsoft.Azure.Cosmos/src/Serializer/CosmosJsonSerializerWrapper.cs#L22
+            // https://github.com/Azure/azure-cosmos-dotnet-v3/blob/0843cae3c252dd49aa8e392623d7eaaed7eb712b/Microsoft.Azure.Cosmos/src/Serializer/CosmosJsonDotNetSerializer.cs#L73
+            using (stream)
+            {
+                // TODO Would be more efficient if CosmosSerializer supported async
+                // See https://github.com/Azure/azure-cosmos-dotnet-v3/issues/715
+                using var memory = new MemoryStream((int)stream.Length);
+                stream.CopyTo(memory);
+
+                byte[] utf8Json = memory.ToArray();
+                return JsonSerializer.Deserialize<T>(utf8Json, _options);
+            }
+        }
+
+        /// <inheritdoc />
+        public override Stream ToStream<T>(T input)
+        {
+            byte[] utf8Json = JsonSerializer.SerializeToUtf8Bytes(input, _options);
+            return new MemoryStream(utf8Json);
+        }
+    }
+}

+ 1 - 0
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -29,6 +29,7 @@
     <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.6.0" />
     <PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
     <PackageReference Include="XC.Framework.Security.RSAUtil" Version="1.0.1" />
+    <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.6.0" />
     <PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="2.0.0-preview" />
   </ItemGroup>
 </Project>

BIN
TEAMModelOS/ClientApp/src/assets/questionItem.png


+ 80 - 0
TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.less

@@ -0,0 +1,80 @@
+@main-bgColor: rgb(40,40,40); //主背景颜色
+@borderColor: #424242;
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+
+.question-item-wrap {
+  background-color: #505050;
+  margin-bottom: 15px;
+  padding: 10px 30px 10px 10px;
+  max-height: 400px;
+  transition: max-height ease 5.5s;
+  position:relative;
+  .toggle-detail-icon {
+    position:absolute;
+    right: 20px;
+    top:10px;
+    cursor: pointer;
+  }
+
+  .toggle-detail-icon-up {
+    transform: rotate(180deg);
+    transition: transform ease 0.5s;
+  }
+
+  .toggle-detail-icon-down {
+    transform: rotate(0deg);
+    transition: transform ease 0.5s;
+  }
+
+  .question-content {
+    color: white;
+    padding-right:80px;
+    .question-order {
+      margin-right: 5px;
+    }
+  }
+
+
+
+  .option-item {
+    margin-top: 10px;
+    color: white;
+
+    .option-order {
+      margin-right: 15px;
+    }
+  }
+}
+
+.question-item-wrap-detail {
+  max-height: 500px;
+}
+
+.question-detail {
+  padding-left: 15px;
+  color: white;
+  transition: height ease 0.5s;
+
+  .answer-label {
+    color: rgb(16, 171, 231);
+    font-weight: 600;
+    border-left: 3px solid rgb(16, 171, 231);
+    padding-left: 8px;
+    line-height: 12px;
+    margin-top: 10px;
+  }
+
+  .answer-detail {
+    padding-left: 12px;
+    margin-top: 5px;
+  }
+}
+.choose-question-btn {
+  position: absolute;
+  right: 60px;
+  top: 10px;
+  cursor: pointer;
+}

+ 77 - 0
TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.vue

@@ -0,0 +1,77 @@
+<template>
+  <div>
+    <div :class="index == openIndex ? 'question-item-wrap-detail question-item-wrap':'question-item-wrap'" v-for="(item,index) in questions">
+      <p class="question-content">
+        <span class="question-order">{{index+1+'.'}}</span>
+        <span class="question-text" v-html='item.question'></span>
+        <Icon v-if="showSelect" type="ios-cart" title="选题" class="choose-question-btn" size="25" @click="selectQuestion(item)"/>
+        <Icon type="ios-arrow-dropdown" :color="index == openIndex? 'cyan':'white'" size="25" @click="toglleQuestionDetail(index)" :class="index == openIndex ? 'toggle-detail-icon toggle-detail-icon-up':'toggle-detail-icon toggle-detail-icon-down'" :title="openIndex == index ? '收起':'查看详情'" />
+      </p>
+      <div v-show="index == openIndex" :class="index == openIndex ? 'question-detail animated  fadeIn':'question-detail animated  fadeOut'">
+        <p class="option-item" v-for="(optionItem,index) in item.option">
+          <span class="option-order">{{optionItem.code+'.'}}</span>
+          <span class="option-text" v-html='optionItem.value'></span>
+        </p>
+        <p class="answer-label">答案:点击展开答案详情</p>
+        <p class="answer-detail">
+          <span v-for="(answerItem,index) in item.answer" v-html='answerItem'></span>
+        </p>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+  export default {
+    props: {
+      questions: {
+        type: Array,
+        default: []
+      },
+      showSelect: { //是否显示选题图标
+        type: Boolean,
+        default:false
+      }
+    },
+    data() {
+      return {
+        groupQuestion: {},
+        questionDetail: false,
+        openIndex: -1
+      }
+    },
+    methods: {
+      selectQuestion(data) {
+        this.$emit('seleteQuestion',data)
+      },
+      groupBy(array, key) {
+        const groups = {}
+        array.forEach(function (item) {
+          const group = JSON.stringify(item[key])
+          groups[group] = groups[group] || []
+          groups[group].push(item)
+        })
+        return Object.keys(groups).map(function (group) {
+          return groups[group]
+        })
+      },
+      toglleQuestionDetail(index) {
+        if (index == this.openIndex) {
+          this.openIndex = -1
+        } else {
+          this.openIndex = index
+        }
+      }
+    },
+    mounted() {
+      console.log('11111')
+      this.groupQuestion = this.groupBy(this.questions,'type')
+      console.log(this.groupQuestion)
+    }
+  }
+</script>
+<style scoped lang="less">
+  @import "./QuestionList.less";
+</style>
+<style>
+
+</style>

+ 17 - 0
TEAMModelOS/ClientApp/src/router/routes.js

@@ -179,7 +179,24 @@ export const routes = [
         path: 'knowledge',
         name: 'knowledge',
         component: resolve => require(['@/view/knowledge-point/index/Index.vue'], resolve)
+      },
+      //新学习活动模块
+      {
+        path: 'createEvaluation',
+        name: 'createEvaluation',
+        component: resolve => require(['@/view/learnactivity/CreateEvaluation.vue'], resolve)
+      },
+      {
+        path: 'createLearnUnit',
+        name: 'createLearnUnit',
+        component: resolve => require(['@/view/selflearning/CreateLearnUnit.vue'], resolve)
+      },
+      {
+        path: 'createOrderLearn',
+        name: 'createOrderLearn',
+        component: resolve => require(['@/view/selflearning/CreateOrderLearn.vue'], resolve)
       }
+
     ]
   }
 ]

+ 4 - 2
TEAMModelOS/ClientApp/src/store/module/schoolBaseInfo.js

@@ -3,7 +3,9 @@ import JSONPath from 'jsonpath'
 export default {
   namespaced: true,
   state: {
-    schoolBaseInfo: undefined,
+    schoolBaseInfo: {
+      period:[]
+    },
     classroomList: undefined,
     demoLoginInfo: {
       user: 'admin',
@@ -27,7 +29,7 @@ export default {
     getSchoolBaseData(context) {
       return new Promise(
         (resolve, reject) => {
-          if (context.state.schoolBaseInfo == undefined) {
+          if (context.state.schoolBaseInfo.period.length == 0) {
             apiTools.schoolSetting.findSchoolSystem({
               schoolCode: context.state.demoLoginInfo.schoolCode
             }).then(

+ 80 - 53
TEAMModelOS/ClientApp/src/view/Home.vue

@@ -34,63 +34,89 @@
 
         <!-- 抽屉菜单栏 -->
         <Menu theme="dark" :open-names="['0']" accordion style="width:100%;padding-left:30px;margin-top:20px;" @on-select="closeMenu">
-            <Submenu name="0">
-                <template slot="title">
-                    <Icon type="ios-paper" size="18" />
-                    <span class="first-menu">学情分析系统</span>
-                </template>
-                <MenuItem name="0-1" to="/home">学情分析仪表盘<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+          <Submenu name="0">
+            <template slot="title">
+              <Icon type="ios-paper" size="18" />
+              <span class="first-menu">学情分析系统</span>
+            </template>
+            <MenuItem name="0-1" to="/home">学情分析仪表盘<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+          </Submenu>
+          <Submenu name="1">
+            <template slot="title">
+              <Icon type="ios-paper" size="18" />
+              <span class="first-menu">智慧校园管理</span>
+            </template>
+            <MenuItem name="1-1" to="/home/system">校园基础数据<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+            <MenuItem name="1-4" to="/home/classroom">班级教室管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+            <MenuItem name="1-2" to="/home/studentAccount">学生账号管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+            <MenuItem name="1-3" to="/home/404">教师账号管理</MenuItem>
+            <MenuItem name="1-5" to="/home/404">软件及授权管理</MenuItem>
+          </Submenu>
+          <Submenu name="2">
+            <template slot="title">
+              <Icon type="ios-people" size="18" />
+              <span class="first-menu">课程管理</span>
+            </template>
+            <MenuItem name="2-2" to="/home/courseManage">课程管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+
+          </Submenu>
+          <Submenu name="3">
+            <template slot="title">
+              <Icon type="ios-folder" size="18" />
+              <span class="first-menu">教材内容管理</span>
+            </template>
+            <MenuItem name="3-1" to="/home/syllabus">课纲管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+            <MenuItem name="3-3" to="/home/teachcontent">内容管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+            <MenuItem name="3-4" to="/home/evaluation/exercisesList">题目/库管理<span style="color:aqua;margin-left:5px;">待完善</span></MenuItem>
+            <MenuItem name="3-5" to="/home/knowledge">知识点管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
+          </Submenu>
+          <Submenu name="4">
+            <template slot="title">
+              <Icon type="md-bug" size="18" />
+              <span class="first-menu">学习活动服务</span>
+            </template>
+            <MenuItem name="4-1" to="/home/evaluation/createTest">创建活动</MenuItem>
+            <MenuItem name="4-2" to="/home/evaluation/testPaperList">管理活动</MenuItem>
+            <MenuItem name="4-3">活动评价</MenuItem>
+            <MenuItem name="4-4">活动仪表</MenuItem>
+          </Submenu>
+          <Submenu name="learnActivity">
+            <template slot="title">
+              <Icon type="md-bug" size="18" />
+              <span class="first-menu">学习活动服务<span style="color:aqua;margin-left:5px;font-size:14px;">(新)</span></span>
+            </template>
+            <Submenu name="create">
+              <template slot="title">
+                <Icon type="ios-create" size="18" />
+                <span class="first-menu">创建学习活动</span>
+              </template>
+              <MenuItem name="createEvaluation" to="/home/createEvaluation">评测活动</MenuItem>
+              <MenuItem name="createUnit" to="/home/createLearnUnit">最小单元</MenuItem>
+              <MenuItem name="createOrder" to="/home/createOrderLearn">编序式活动</MenuItem>
+              <MenuItem name="createPreview" to="/404">课前预习</MenuItem>
+              <MenuItem name="createHomework" to="/404">作业活动</MenuItem>
             </Submenu>
-            <Submenu name="1">
+            <Submenu name="manage">
               <template slot="title">
-                <Icon type="ios-paper" size="18" />
-                <span class="first-menu">智慧校园管理</span>
+                <Icon type="md-list" size="18" />
+                <span class="first-menu">管理学习活动</span>
               </template>
-              <MenuItem name="1-1" to="/home/system">校园基础数据<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
-              <MenuItem name="1-4" to="/home/classroom">班级教室管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
-              <MenuItem name="1-2" to="/home/studentAccount">学生账号管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
-              <MenuItem name="1-3" to="/home/404">教师账号管理</MenuItem>
-              <MenuItem name="1-5" to="/home/404">软件及授权管理</MenuItem>
+              <MenuItem name="manageEvaluation" to="/home/createEvaluation">评测活动</MenuItem>
+              <MenuItem name="manageLearn" to="/home/404">自主学习</MenuItem>
+              <MenuItem name="manageWork" to="/home/manageActivity">作业活动</MenuItem>
             </Submenu>
-            <Submenu name="2">
-                <template slot="title">
-                    <Icon type="ios-people" size="18" />
-                    <span class="first-menu">课程管理</span>
-                </template>
-                <MenuItem name="2-2" to="/home/courseManage">课程管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
 
-            </Submenu>
-            <Submenu name="3">
-                <template slot="title">
-                    <Icon type="ios-folder" size="18" />
-                    <span class="first-menu">教材内容管理</span>
-                </template>
-                <MenuItem name="3-1" to="/home/syllabus">课纲管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
-                <!--<MenuItem name="3-2">校本课纲管理</MenuItem>-->
-                <MenuItem name="3-3" to="/home/teachcontent">内容管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
-                <MenuItem name="3-4" to="/home/evaluation/exercisesList">题目/库管理<span style="color:aqua;margin-left:5px;">待完善</span></MenuItem>
-                <MenuItem name="3-5" to="/home/knowledge">知识点管理<span style="color:aqua;margin-left:5px;">√</span></MenuItem>
-            </Submenu>
-            <Submenu name="4">
-                <template slot="title">
-                    <Icon type="md-bug" size="18" />
-                    <span class="first-menu">学习活动服务</span>
-                </template>
-                <MenuItem name="4-1" to="/home/evaluation/createTest">创建活动</MenuItem>
-                <MenuItem name="4-2" to="/home/evaluation/testPaperList">管理活动</MenuItem>
-                <MenuItem name="4-3">活动评价</MenuItem>
-                <MenuItem name="4-4">活动仪表</MenuItem>
-            </Submenu>
-            <Submenu name="5">
-                <template slot="title">
-                    <Icon type="ios-stats" size="18" />
-                    <span class="first-menu">班级智慧服务</span>
-                </template>
-                <MenuItem name="5-1">区班校活动记录簿</MenuItem>
-                <MenuItem name="5-3">操行成绩管理</MenuItem>
-                <MenuItem name="5-4">成绩总览与登录</MenuItem>
-                <MenuItem name="5-5" to="/totalIndex">学情仪表盘</MenuItem>
-            </Submenu>
+          </Submenu>
+          <Submenu name="5">
+            <template slot="title">
+              <Icon type="ios-stats" size="18" />
+              <span class="first-menu">班级智慧服务</span>
+            </template>
+            <MenuItem name="5-1">区班校活动记录簿</MenuItem>
+            <MenuItem name="5-3">操行成绩管理</MenuItem>
+            <MenuItem name="5-4">成绩总览与登录</MenuItem>
+            <MenuItem name="5-5" to="/totalIndex">学情仪表盘</MenuItem>
+          </Submenu>
         </Menu>
 
       </Drawer>
@@ -113,9 +139,10 @@
       }
     },
     created() {
-
+      //this.getSchoolBaseInfo()
     },
     methods: {
+      
       closeMenu() {
         this.isOpenDrawer = false
       },

+ 5 - 5
TEAMModelOS/ClientApp/src/view/coursemgmt/CourseClassroom.vue

@@ -130,13 +130,13 @@
              @on-ok="comfirmCustomRules"
              @on-cancel="cancel" class="custom-group">
         <Form :label-width="80" :label-colon="true">
-          <FormItem label="$t('courseManage.classroom.studentCountLabel')">
+          <FormItem :label="$t('courseManage.classroom.studentCountLabel')">
             <span>{{classroomStudent.length}}人</span>
           </FormItem>
-          <FormItem label="$t('courseManage.classroom.groupCountLabel')">
+          <FormItem :label="$t('courseManage.classroom.groupCountLabel')">
             <InputNumber :max="10" :min="1" v-model="groupNum"></InputNumber>
           </FormItem>
-          <FormItem label="$t('courseManage.classroom.groupTypeLabel')">
+          <FormItem :label="$t('courseManage.classroom.groupTypeLabel')">
             <RadioGroup v-model="groupType">
               <Radio label="1">
                 <span>{{$t('courseManage.classroom.groupType1')}}</span>
@@ -190,8 +190,8 @@
           notice:''
         },
         newPersonalStatus:false,
-        groupNum: 0,
-        groupType: 0,
+        groupNum: 2,
+        groupType: '1',
         selections: [],
         customGroupStatus: false,
         currentGroup: '',

+ 1 - 0
TEAMModelOS/ClientApp/src/view/coursemgmt/CourseManage.vue

@@ -272,6 +272,7 @@
     border-radius: 0px 0px 5px 5px;
     overflow-y: auto !important;
     height: 220px !important;
+    z-index:99 !important;
   }
 
   .course-base-info-content .w-e-text {

+ 0 - 2
TEAMModelOS/ClientApp/src/view/evaluation/index/CreateTest.css

@@ -159,8 +159,6 @@ exersices-attr-diff {
     font-size:14px;
 }
     .my-check-button:hover {
-        /*background: #2D8CF0;*/
-        /*color: #2D8CF0;*/
         border: 1px solid #2D8CF0;
         font-weight:600;
     }

+ 47 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreate.less

@@ -0,0 +1,47 @@
+@main-bgColor: rgb(40,40,40); //主背景颜色
+@borderColor: #424242;
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+
+
+.question-condition-wrap {
+  width: 900px;
+  margin: auto;
+  margin-top: 25px;
+
+  .question-condition-item {
+    color: white;
+    font-size: 16px;
+    margin-top: 25px;
+
+    .condition-label {
+      /*margin-right: 15px;*/
+      display: inline-block;
+      color: @second-textColor;
+      width: 95px;
+    }
+
+    .question-num-item {
+      margin-bottom:25px;
+      /*display: inline-block;*/
+      color: @primary-textColor;
+      .question-type-label {
+        margin-right: 10px;
+      }
+
+      .input-tag-text {
+        color: @second-textColor;
+        display: inline-block;
+        margin: 0px 6px;
+      }
+
+      .question-difficulty-label{
+        margin-left:30px;
+        display:inline-block;
+        margin-right:10px;
+      }
+    }
+  }
+}

+ 266 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreate.vue

@@ -0,0 +1,266 @@
+<template>
+  <div style="height:100%;background:#404040;">
+    <vuescroll>
+      <h2 style="text-align:center;margin-top:30px;color:#CCCCCC;">设置组题条件</h2>
+      <div class="question-condition-wrap">
+        <div class="question-condition-item">
+          <span class="condition-label">题库范围:</span>
+          <CheckboxGroup v-model="autoCreateFilter.scope" style="display:inline-block;">
+            <Checkbox label="私有题库" style="margin-right:65px;"></Checkbox>
+            <Checkbox label="校本题库"></Checkbox>
+          </CheckboxGroup>
+        </div>
+        <div class="question-condition-item">
+          <span class="condition-label">适用学段:</span>
+          <CheckboxGroup v-model="autoCreateFilter.period" style="display:inline-block;">
+            <Checkbox label="小学" style="margin-right:93px;"></Checkbox>
+            <Checkbox label="初中" style="margin-right:93px;"></Checkbox>
+            <Checkbox label="高中"></Checkbox>
+          </CheckboxGroup>
+        </div>
+        <div class="question-condition-item">
+          <span class="condition-label">题型设置:</span>
+          <CheckboxGroup v-model="autoCreateFilter.questionType" style="display:inline-block;">
+            <Checkbox label="Single" style="margin-right:80px;">单选题</Checkbox>
+            <Checkbox label="Multiple" style="margin-right:80px;">多选题</Checkbox>
+            <Checkbox label="Judge" style="margin-right:80px;">判断题</Checkbox>
+            <Checkbox label="Complete" style="margin-right:70px;">填空题</Checkbox>
+            <Checkbox label="Subjective" style="margin-right:70px;">问答题</Checkbox>
+            <Checkbox label="Compose">综合题</Checkbox>
+          </CheckboxGroup>
+        </div>
+        <!--<Divider dashed />-->
+        <div class="question-condition-item">
+          <span class="condition-label" style="vertical-align:top;">题量设置:</span>
+          <div style="display:inline-block;width:calc(100% - 95px);">
+            <div :class="autoCreateFilter.questionType.indexOf('Single') != -1 ? 'question-num-item':'question-num-item'" v-if="autoCreateFilter.questionType.indexOf('Single') != -1">
+              <span class="question-type-label">单选题:</span>
+              <span class="input-tag-text">共</span>
+              <Input v-model="single.total" placeholder="0" style="width: 40px" size="small" />
+              <span class="input-tag-text">道</span>
+              <span class="question-difficulty-label">难度设置:</span>
+              <Select v-model="single.difficultyType" style="width:100px" size="small">
+                <Option v-for="(item,index) in difficultyList" :value="item.value" :key="index">{{ item.label }}</Option>
+              </Select>
+              <div style="display:inline-block;" v-if="single.difficultyType == 'custom'" :class="single.difficultyType == 'custom' ? 'animated fadeIn':''">
+                <span class="input-tag-text" style="margin-left:30px;">简单</span>
+                <Input v-model="single.simple" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">一般</span>
+                <Input v-model="single.common" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="    margin-left: 30px;">困难</span>
+                <Input v-model="single.difficulty" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+              </div>
+            </div>
+            <div :class="autoCreateFilter.questionType.indexOf('Multiple') != -1 ? 'question-num-item':'question-num-item'" v-if="autoCreateFilter.questionType.indexOf('Multiple') != -1">
+              <span class="question-type-label">多选题:</span>
+              <span class="input-tag-text">共</span>
+              <Input v-model="multiple.total" placeholder="0" style="width: 40px" size="small" />
+              <span class="input-tag-text">道</span>
+              <span class="question-difficulty-label">难度设置:</span>
+              <Select v-model="multiple.difficultyType" style="width:100px" size="small">
+                <Option v-for="(item,index) in difficultyList" :value="item.value" :key="index">{{ item.label }}</Option>
+              </Select>
+              <div style="display:inline-block;" v-if="multiple.difficultyType == 'custom'" :class="multiple.difficultyType == 'custom' ? 'animated fadeIn':''">
+                <span class="input-tag-text" style="margin-left:30px;">简单</span>
+                <Input v-model="multiple.simple" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">一般</span>
+                <Input v-model="multiple.common" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">困难</span>
+                <Input v-model="multiple.difficulty" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+              </div>
+            </div>
+            <div :class="autoCreateFilter.questionType.indexOf('Judge') != -1 ? 'question-num-item':'question-num-item'" v-if="autoCreateFilter.questionType.indexOf('Judge') != -1">
+              <span class="question-type-label">判断题:</span>
+              <span class="input-tag-text">共</span>
+              <Input v-model="judge.total" placeholder="0" style="width: 40px" size="small" />
+              <span class="input-tag-text">道</span>
+              <span class="question-difficulty-label">难度设置:</span>
+              <Select v-model="judge.difficultyType" style="width:100px" size="small">
+                <Option v-for="(item,index) in difficultyList" :value="item.value" :key="index">{{ item.label }}</Option>
+              </Select>
+              <div style="display:inline-block;" v-if="judge.difficultyType == 'custom'" :class="judge.difficultyType == 'custom' ? 'animated fadeIn':''">
+                <span class="input-tag-text" style="margin-left:30px;">简单</span>
+                <Input v-model="judge.simple" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">一般</span>
+                <Input v-model="judge.common" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">困难</span>
+                <Input v-model="judge.difficulty" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+              </div>
+              
+            </div>
+            <div :class="autoCreateFilter.questionType.indexOf('Complete') != -1 ? 'question-num-item':'question-num-item'" v-if="autoCreateFilter.questionType.indexOf('Complete') != -1">
+              <span class="question-type-label">填空题:</span>
+              <span class="input-tag-text">共</span>
+              <Input v-model="complete.total" placeholder="0" style="width: 40px" size="small" />
+              <span class="input-tag-text">道</span>
+              <span class="question-difficulty-label">难度设置:</span>
+              <Select v-model="complete.difficultyType" style="width:100px" size="small">
+                <Option v-for="(item,index) in difficultyList" :value="item.value" :key="index">{{ item.label }}</Option>
+              </Select>
+              <div style="display:inline-block;" v-if="complete.difficultyType == 'custom'" :class="complete.difficultyType == 'custom' ? 'animated fadeIn':''">
+                <span class="input-tag-text" style="margin-left:30px;">简单</span>
+                <Input v-model="complete.simple" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">一般</span>
+                <Input v-model="complete.common" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">困难</span>
+                <Input v-model="complete.difficulty" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+              </div>
+            </div>
+            <div :class="autoCreateFilter.questionType.indexOf('Subjective') != -1 ? 'question-num-item':'question-num-item'" v-if="autoCreateFilter.questionType.indexOf('Subjective') != -1">
+              <span class="question-type-label">问答题:</span>
+              <span class="input-tag-text">共</span>
+              <Input v-model="subjective.total" placeholder="0" style="width: 40px" size="small" />
+              <span class="input-tag-text">道</span>
+              <span class="question-difficulty-label">难度设置:</span>
+              <Select v-model="subjective.difficultyType" style="width:100px" size="small">
+                <Option v-for="(item,index) in difficultyList" :value="item.value" :key="index">{{ item.label }}</Option>
+              </Select>
+              <div style="display:inline-block;" v-if="subjective.difficultyType == 'custom'" :class="subjective.difficultyType == 'custom' ? 'animated fadeIn':''">
+                <span class="input-tag-text" style="margin-left:30px;">简单</span>
+                <Input v-model="subjective.simple" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">一般</span>
+                <Input v-model="subjective.common" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">困难</span>
+                <Input v-model="subjective.difficulty" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+              </div>
+            </div>
+            <div :class="autoCreateFilter.questionType.indexOf('Compose') != -1 ? 'question-num-item':'question-num-item'" v-if="autoCreateFilter.questionType.indexOf('Compose') != -1">
+              <span class="question-type-label">综合题:</span>
+              <span class="input-tag-text">共</span>
+              <Input v-model="compose.total" placeholder="0" style="width: 40px" size="small" />
+              <span class="input-tag-text">道</span>
+              <span class="question-difficulty-label">难度设置:</span>
+              <Select v-model="compose.difficultyType" style="width:100px" size="small">
+                <Option v-for="(item,index) in difficultyList" :value="item.value" :key="index">{{ item.label }}</Option>
+              </Select>
+              <div style="display:inline-block;" v-if="compose.difficultyType == 'custom'" :class="compose.difficultyType == 'custom' ? 'animated fadeIn':''">
+                <span class="input-tag-text" style="margin-left:30px;">简单</span>
+                <Input v-model="compose.simple" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">一般</span>
+                <Input v-model="compose.common" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+                <span class="input-tag-text" style="margin-left:30px;">困难</span>
+                <Input v-model="compose.difficulty" placeholder="0" style="width: 40px" size="small" />
+                <span class="input-tag-text">道</span>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="question-condition-item" style="margin-top:0px;">
+          <span class="condition-label">知识点:</span>
+          <Tag closable color="#404040">知识点1</Tag>
+          <Tag closable color="#404040">知识点2</Tag>
+          <Tag closable color="#404040">知识点3</Tag>
+          <Icon type="md-add-circle" color="white" size="18" style="margin-left:15px;" />
+        </div>
+        <div style="text-align:center;margin-top:50px;">
+          <Button type="info" style="width:50%;">开始组卷</Button>
+        </div>
+      </div>
+    </vuescroll>
+  </div>
+</template>
+<script>
+  export default {
+    data() {
+      return {
+        single: {
+          total: 0,
+          difficultyType: 'random',
+          simple: 0,
+          common: 0,
+          difficulty: 0
+        },
+        multiple: {
+          total: 0,
+          difficultyType: 'random',
+          simple: 0,
+          common: 0,
+          difficulty: 0
+        },
+        judge: {
+          total: 0,
+          difficultyType: 'random',
+          simple: 0,
+          common: 0,
+          difficulty: 0
+        },
+        complete: {
+          total: 0,
+          difficultyType: 'random',
+          simple: 0,
+          common: 0,
+          difficulty: 0
+        },
+        subjective: {
+          total: 0,
+          difficultyType: 'random',
+          simple: 0,
+          common: 0,
+          difficulty: 0
+        },
+        compose: {
+          total: 0,
+          difficultyType: 'random',
+          simple: 0,
+          common: 0,
+          difficulty: 0
+        },
+        autoCreateFilter: {
+          scope: [],
+          period: [],
+          questionType: ['Single'],
+          questionNum: {},
+          knowledges:[]
+        },
+        difficultyList: [
+          {
+            value: 'random',
+            label:'随机'
+          },
+          {
+            value: 'average',
+            label:'平均分配'
+          },
+          {
+            value: 'custom',
+            label:'自定义'
+          }
+        ]
+      }
+    }
+  }
+</script>
+<style lang="less">
+  @import "./AutoCreate.less";
+</style>
+<style>
+  .question-num-item .ivu-input {
+    background:none;
+    text-align:center;
+    color:white;
+    font-size:18px;
+  }
+  .question-num-item .ivu-select-selection {
+    background: none;
+    color: white;
+    border-color: #dcdee2;
+  }
+</style>

+ 118 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/CreateEvaluation.less

@@ -0,0 +1,118 @@
+@main-bgColor: rgb(40,40,40); //主背景颜色
+@borderColor: #424242;
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+
+.create-evaluation-container {
+  width: 100%;
+  height: 100%;
+
+  .create-header {
+    width: 100%;
+    height: 45px;
+    border-bottom: 1px solid @borderColor;
+
+    .create-header-title {
+      height: 45px;
+      line-height: 45px;
+      width: 100%;
+      color: @primary-textColor;
+      padding-left: 20px;
+      font-size: 16px;
+      width: 150px;
+      display:inline-block;
+    }
+
+    .btn-save {
+      color: rgb(107, 223, 195);
+      float: right;
+      cursor:pointer;
+      line-height:45px;
+      margin-right:40px;
+    }
+  }
+
+  .create-body {
+    width: 100%;
+    height: ~"calc(100% - 45px)";
+    display: flex;
+    flex-direction: row;
+  }
+}
+.create-body {
+  .evaluation-attr-wrap {
+    width: 400px;
+    border-right: 1px solid @borderColor;
+    height: 100%;
+    padding-left: 20px;
+
+    .wrap-label {
+      color: white;
+      font-size: @primary-fontSize;
+      height: 40px;
+      line-height: 40px;
+      border-bottom: 1px solid @borderColor;
+    }
+  }
+
+  .evaluation-question-wrap {
+    width: ~"calc(100% - 400px)";
+    height: 100%;
+    padding-left: 20px;
+
+    .wrap-label {
+      color: white;
+      font-size: @primary-fontSize;
+      height: 40px;
+      line-height: 40px;
+      border-bottom: 1px solid @borderColor;
+
+      p {
+        display: inline-block;
+        margin-right:25px;
+      }
+
+      .subject-item {
+        display: inline-block;
+        margin-right: 30px;
+        color:@second-textColor;
+        cursor:pointer;
+        line-height:39px;
+        min-width:50px;
+        font-size:16px;
+        text-align:center;
+      }
+
+      .subject-item-active{
+        color:@primary-textColor;
+        border-bottom:2px solid white; 
+        font-weight:600;
+      }
+    }
+  }
+}
+.evaluation-attr-form{
+  /*margin-top:30px;*/
+  margin-right:10px;
+  
+}
+.evaluation-question-main {
+  width: 100%;
+  height:~"calc(100% - 40px)";
+  padding-top: 15px;
+  .create-type-wrap{
+    color:white;
+  }
+}
+.add-subject-icon {
+  /*float: right;*/
+  /*margin-right: 50px;*/
+  margin-top: 8px;
+  cursor: pointer;
+}
+.question-main-tabs {
+  padding-top: 15px;
+  height: ~"calc(100% - 45px)";
+}

+ 302 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/CreateEvaluation.vue

@@ -0,0 +1,302 @@
+<template>
+  <div class="create-evaluation-container">
+    <div class="create-header">
+      <p class="create-header-title">创建评测活动</p>
+      <p class="btn-save" @click="saveEvaluation"><Icon type="ios-albums-outline" color="#6BE9C3" style="margin-right:10px;" />保存评测</p>
+    </div>
+    <div class="create-body">
+      <div class="evaluation-attr-wrap">
+        <p class="wrap-label">基础信息</p>
+        <div style="width:100%; height:calc(100% - 45px);padding-top:30px;">
+          <!--<vuescroll>-->
+          <Form :model="evaluationInfo" label-position="top" class="evaluation-attr-form" label-colon>
+            <FormItem label="评测名称">
+              <Input v-model="evaluationInfo.evaluationName"></Input>
+            </FormItem>
+            <FormItem label="测试类型">
+              <Select v-model="evaluationInfo.type">
+                <Option v-for="(item,index) in typeList" :value="item" :key="index">{{ item }}</Option>
+              </Select>
+            </FormItem>
+            <FormItem label="发布方式">
+              <Select v-model="evaluationInfo.publishType">
+                <Option v-for="(item,index) in publishTypeList" :value="item" :key="index">{{ item }}</Option>
+              </Select>
+            </FormItem>
+            <FormItem label="施测对象">
+              <Select v-model="evaluationInfo.target">
+                <Option v-for="(item,index) in classroomList" :value="item" :key="index">{{ item }}</Option>
+              </Select>
+            </FormItem>
+            <FormItem label="施测时间">
+              <DatePicker type="daterange" split-panels placeholder="请选择发布时间" style="width:100%"></DatePicker>
+            </FormItem>
+          </Form>
+          <!--</vuescroll>-->
+        </div>
+      </div>
+      <div class="evaluation-question-wrap">
+        <div class="wrap-label">
+          <p>测试科目:</p>
+          <span v-for="(item,index) in evaluationInfo.testPaper" :class="index == currentSubjectIndex ? 'subject-item subject-item-active':'subject-item'" @click="selectSubject(index)">{{item.subject}}</span>
+          <Icon @click="addSubject" type="md-add-circle" title="添加科目" color="white" class="add-subject-icon" size="20" />
+        </div>
+        <div class="evaluation-question-main">
+          <div class="create-type-wrap">
+            <span>创建方式:</span>
+            <RadioGroup v-model="evaluationInfo.testPaper[currentSubjectIndex].createType" style="margin-left:25px;" @on-change="setActiveTab">
+              <Radio label="auto">自动组题</Radio>
+              <Radio label="manual">手动挑题/卷</Radio>
+              <Radio label="import">试卷导入</Radio>
+            </RadioGroup>
+          </div>
+          <Tabs :value="activeTab" type="card" class="question-main-tabs">
+            <TabPane label="组题条件" name="auto" v-if="evaluationInfo.testPaper[currentSubjectIndex].createType == 'auto'">
+              <AutoCreate></AutoCreate>
+            </TabPane>
+            <TabPane label="备选题目" name="manual" v-if="evaluationInfo.testPaper[currentSubjectIndex].createType == 'manual'">
+              <ManualCreate :questionList="questionList"></ManualCreate>
+            </TabPane>
+            <TabPane label="导入说明" name="import" v-if="evaluationInfo.testPaper[currentSubjectIndex].createType == 'import'">
+              <ImportCreate></ImportCreate>
+            </TabPane>
+            <TabPane label="试题预览" name="preview">
+              <TeacherPreview :questionList="questionList"></TeacherPreview>
+            </TabPane>
+            <TabPane label="学生作答预览" name="student" >
+              <StudentPreview></StudentPreview>
+            </TabPane>
+          </Tabs>
+        </div>
+      </div>
+    </div>
+    <Modal v-model="addSubjectStatus"
+           title="添加测试学科"
+           @on-ok="confirmAddSubject"
+           @on-cancel="cancelAddSubject">
+      <!--<div v-for="(item,index) in $store.state.schoolBaseInfo.schoolBaseInfo.period" style="margin-top:10px;margin-bottom:30px;">
+        <div style="padding-bottom:6px;margin-bottom:6px;">
+          <span>{{item.periodName}}</span>
+        </div>
+        <CheckboxGroup v-model="checkAllGroup" @on-change="checkAllGroupChange">
+          <Checkbox :label="subjectItem.subjectCode" v-for="(subjectItem,index) in item.subjects">{{subjectItem.subjectName}}</Checkbox>
+        </CheckboxGroup>
+      </div>-->
+        <CheckboxGroup v-model="evaluationSubjects" @on-change="checkAllGroupChange">
+          <div v-for="(item,index) in $store.state.schoolBaseInfo.schoolBaseInfo.period" style="margin-bottom: 20px;">
+            <p>{{item.periodName}}</p>
+            <Checkbox :label="subjectItem.subjectCode" v-for="(subjectItem,index) in item.subjects">{{subjectItem.subjectName}}</Checkbox>
+          </div>
+        </CheckboxGroup>
+
+    </Modal>
+  </div>
+</template>
+<script>
+  import questions from '../evaluation/index/list.json'
+  import AutoCreate from './AutoCreate.vue'
+  import ManualCreate from './ManualCreate.vue'
+  import ImportCreate from './ImportCreate.vue'
+  import TeacherPreview from './TeacherPreview.vue'
+  import StudentPreview from './StudentPreview.vue'
+  import NoData from '@/common/NoData.vue'
+  export default {
+    components: {
+      NoData,
+      AutoCreate,
+      ManualCreate,
+      TeacherPreview,
+      StudentPreview,
+      ImportCreate
+    },
+    data() {
+      return {
+        activeTab:'auto',
+        evaluationSubjects:[],
+        subjectList:[],
+        addSubjectStatus:false,
+        autoFilter: {},
+        manualFilter: {},
+        importFilter: {},
+        questionList:[],
+        currentSubjectIndex: 0,
+        evaluationInfo: {
+          evaluationName: '',
+          testPaper: [
+            {
+              subject: '语文',
+              createType: 'auto',
+              filter: {}
+            },
+            {
+              subject: '数学',
+              createType: 'manual'
+            },
+          ],
+          target: '',
+          type: '',
+          publishType: '',
+          publishTime: '',
+        },
+        periodList: [
+          '小学',
+          '初中',
+          '高中'
+        ],
+        subjectList: [
+          '语文',
+          '数学',
+          '英语'
+        ],
+        classroomList: [
+          '一年级一班',
+          '一年级二班',
+          '一年级三班'
+        ],
+        semesterList: [
+          '上学期',
+          '下学期'
+        ],
+        rangeList: [
+          '班级测试',
+          '校级测试',
+          '学区测试'
+        ],
+        typeList: [
+          '周考',
+          '期初考',
+          '期中考',
+          '期末考',
+          '诊断测验',
+          '模拟测验'
+        ],
+        publishTypeList: [
+          '自动发布',
+          '手动发布'
+        ],
+        createTypeList: [
+          '自动组题',
+          '手动挑题',
+          '模板导入',
+          '挑选试卷'
+        ]
+
+      }
+    },
+    methods: {
+      inDevelopment() {
+        this.$Message.info('功能正在开发中,敬请期待!')
+      },
+      setActiveTab(data) {
+        console.log(data)
+        this.activeTab = data
+      },
+      confirmAddSubject() {
+        console.log(this.evaluationSubjects)
+      },
+      cancelAddSubject() {
+
+      },
+      getSchoolBaseInfo() {
+        this.$store.dispatch('schoolBaseInfo/getSchoolBaseData').then(
+          (res) => {
+            if (res.code == 2) {
+              alert('数据为空!')
+            } else {
+              
+            }
+          },
+          (err) => {
+            alert('API error!')
+          }
+        )
+      },
+      selectSubject(index) {
+        this.currentSubjectIndex = index
+        this.activeTab = this.evaluationInfo.testPaper[index].createType
+      },
+      saveEvaluation() {
+        console.log('........')
+        console.log(this.evaluationInfo)
+      },
+      addSubject() {
+        this.addSubjectStatus = true
+      },
+      handleCheckAll() {
+        if (this.indeterminate) {
+          this.checkAll = false;
+        } else {
+          this.checkAll = !this.checkAll;
+        }
+        this.indeterminate = false;
+
+        if (this.checkAll) {
+          this.checkAllGroup = ['香蕉', '苹果', '西瓜'];
+        } else {
+          this.checkAllGroup = [];
+        }
+      },
+      checkAllGroupChange(data) {
+        if (data.length === 3) {
+          this.indeterminate = false;
+          this.checkAll = true;
+        } else if (data.length > 0) {
+          this.indeterminate = true;
+          this.checkAll = false;
+        } else {
+          this.indeterminate = false;
+          this.checkAll = false;
+        }
+      }
+    },
+    created() {
+      this.getSchoolBaseInfo()
+      this.questionList = questions.result.data
+      
+    }
+  }
+</script>
+<style scoped lang="less">
+  @import "./CreateEvaluation.less";
+</style>
+<style>
+  .evaluation-attr-form .ivu-input, .evaluation-attr-form .ivu-select-selection {
+    background: none;
+    color: white;
+    border-color: #999999;
+    font-size:16px;
+  }
+
+  .evaluation-attr-form .ivu-input {
+    /*border:none;
+    border-bottom:1px solid #424242;
+    border-radius:0px;*/
+  }
+
+  .evaluation-attr-wrap .ivu-form .ivu-form-item-label {
+    color: #a5a5a5;
+  }
+
+  .evaluation-question-main .ivu-tabs-bar {
+    border-color: #404040;
+  }
+
+  .evaluation-question-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab {
+    border: none;
+    background-color: #303030;
+    color: white;
+    margin-right: 2px;
+  }
+
+  .evaluation-question-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab-active {
+    background-color: #404040;
+    color: white;
+    font-weight:600;
+  }
+  .evaluation-question-main .ivu-tabs .ivu-tabs-content-animated {
+    height:100%;
+  }
+  .evaluation-question-main .ivu-tabs-bar {
+    margin-bottom:0px;
+  }
+</style> 

+ 42 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/ImportCreate.less

@@ -0,0 +1,42 @@
+@main-bgColor: rgb(40,40,40); //主背景颜色
+@borderColor: #424242;
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+
+.import-title {
+  color: @second-textColor;
+  text-align: center;
+  padding-top: 55px;
+  font-size:24px;
+  a {
+    font-size: 15px;
+    border-bottom: 1px solid #2d8cf0;
+    margin-left:10px;
+  }
+
+}
+.import-discreption-wrap{
+  max-width:1000px;
+  min-width:800px;
+  width:68%;
+  margin:auto;
+  margin-top:30px;
+  color:@second-textColor;
+  font-size:18px;
+  p{
+    margin-bottom:15px;
+  }
+}
+.upload-wrap {
+  max-width: 1000px;
+  min-width: 800px;
+  width: 68%;
+  margin:auto;
+  margin-top:30px;
+  color:#AAAAAA;
+}
+.upload-wrap:hover {
+  color: white;
+}

+ 42 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/ImportCreate.vue

@@ -0,0 +1,42 @@
+<template>
+  <div style="height: 100%; background: #404040;" class="import-create-wrap">
+    <vuescroll>
+      <h2 class="import-title">
+        试卷导入说明
+        <a href="#">下载模板</a>
+      </h2>
+      <div class="import-discreption-wrap">
+        <p>1、暂时只支持“.docx、.xlsx、.csv”格式的文件导入,文件大小不超过10M,需要按照模板格式导入;</p>
+        <p>2、导入的题型暂时只支持单选题、多选题、判断题、填空题、主观题。</p>
+      </div>
+      <Upload type="drag" action="" multiple :before-upload="beforeUpload" class="upload-wrap">
+        <p style="margin:20px 0px; margin-top:50px;font-size:30px;">{{$t('teachContent.uploadText')}}</p>
+        <Icon type="ios-cloud-upload" size="80" style="margin-bottom:50px;"/>
+      </Upload>
+    </vuescroll>
+</div>
+</template>
+<script>
+  export default {
+    data() {
+      return {
+
+      }
+    }
+  }
+</script>
+<style lang="less">
+  @import "./ImportCreate.less";
+</style>
+<style>
+  .upload-wrap .ivu-upload-drag {
+    background: #606060;
+  }
+  .upload-wrap:hover .ivu-upload-drag {
+    background: #505050;
+    border-color:white;
+    transition: background ease 0.5s;
+    transition: color ease 0.5s;
+  }
+
+</style>

+ 50 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.less

@@ -0,0 +1,50 @@
+@main-bgColor: rgb(40,40,40); //主背景颜色
+@borderColor: #424242;
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+
+.manual-filter-wrap {
+  width: 100%;
+  border-bottom: 1px dashed #606060;
+  padding: 15px 15px 0px 15px;
+  color: white;
+  display: flex;
+  flex-direction: row;
+
+  &-left {
+    width: 50%;
+  }
+
+  &-right {
+    width: 50%;
+  }
+
+  .manual-filter-item {
+    margin-top:5px;
+    margin-bottom:20px;
+    .manual-filter-label {
+      width:80px;
+      display:inline-block;
+      color: @second-textColor;
+    }
+  }
+}
+
+.question-list-wrap {
+  padding: 10px;
+  padding-bottom: 30px;
+  position: relative;
+
+  .page-wrap {
+    width: 100%;
+    position: fixed;
+    left: 0px;
+    bottom: 32px;
+    padding: 5px 0px;
+    background: rgba(20,20,20,.8);
+    text-align: center;
+    color:white;
+  }
+}

+ 73 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.vue

@@ -0,0 +1,73 @@
+<template>
+  <div style="height: 100%;background: #404040;">
+    <vuescroll>
+      <div class="manual-filter-wrap">
+        <div class="manual-filter-wrap-left">
+          <div class="manual-filter-item">
+            <span class="manual-filter-label">学段:</span>
+            <CheckboxGroup v-model="fruit" style="display:inline-block;">
+              <Checkbox label="">小学</Checkbox>
+              <Checkbox label="">初中</Checkbox>
+              <Checkbox label="">高中</Checkbox>
+            </CheckboxGroup>
+          </div>
+          <div class="manual-filter-item">
+            <span class="manual-filter-label">知识点:</span>
+            <span>暂无知识点</span>
+            <Icon @click="addKnowledge" type="md-add-circle" title="添加知识点" color="white" style="margin-left:15px;cursor:pointer;" size="18" />
+          </div>
+        </div>
+        <div class="manual-filter-wrap-right">
+          <div class="manual-filter-item">
+            <span class="manual-filter-label">题库范围:</span>
+            <CheckboxGroup v-model="fruit" style="display:inline-block;">
+              <Checkbox label="">私有题库</Checkbox>
+              <Checkbox label="">校本题库</Checkbox>
+            </CheckboxGroup>
+          </div>
+        </div>
+      </div>
+      <div class="question-list-wrap">
+        <QuestionList :showSelect="true" :questions="questionList"></QuestionList>
+        <div class="page-wrap">
+          <Page :total="40" size="small" show-elevator show-sizer />
+        </div>
+      </div>
+    </vuescroll>
+    <Modal v-model="addKnowledgeStatus"
+           title="设置知识点"
+           @on-ok="ok"
+           @on-cancel="cancel">
+      <p>Content of dialog</p>
+      <p>Content of dialog</p>
+      <p>Content of dialog</p>
+    </Modal>
+  </div>
+</template>
+<script>
+  import QuestionList from  '@/components/learnactivity/QuestionList.vue'
+  export default {
+    components: {
+      QuestionList
+    },
+    props: {
+      questionList: {
+        type: Array,
+        default:[]
+      }
+    },
+    data() {
+      return {
+        addKnowledgeStatus:false
+      }
+    },
+    methods: {
+      addKnowledge() {
+        this.addKnowledgeStatus = true
+      }
+    }
+  }
+</script>
+<style lang="less">
+  @import "./ManualCreate.less";
+</style>

TEAMModelOS/ClientApp/src/view/learnactivity/index.less → TEAMModelOS/ClientApp/src/view/learnactivity/StudentPreview.less


+ 17 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/StudentPreview.vue

@@ -0,0 +1,17 @@
+<template>
+  <div style="height: 100%;background: #404040;padding-top: 240px;">
+    <h1 style="text-align:center;color:#AAA;">功能正在开发中,敬请期待!</h1>
+  </div>
+</template>
+<script>
+  export default {
+    data() {
+      return {
+
+      }
+    }
+  }
+</script>
+<style lang="less">
+  @import "./AutoCreate.less";
+</style>

TEAMModelOS/ClientApp/src/view/learnactivity/index.vue → TEAMModelOS/ClientApp/src/view/learnactivity/TeacherPreview.less


+ 58 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/TeacherPreview.vue

@@ -0,0 +1,58 @@
+<template>
+  <div style="height: 100%;
+    background: #404040;
+    padding: 10px;
+    padding-bottom: 30px;">
+    <vuescroll>
+      <h2 style="text-align:center;color:white;">一次函数的性质周末测试</h2>
+      <!--<div :class="index == openIndex ? 'question-item-wrap-detail question-item-wrap':'question-item-wrap'" v-for="(item,index) in questionList">
+        <p class="question-content">
+          <span class="question-order">{{index+1+'.'}}</span>
+          <span class="question-text" v-html='item.question'></span>
+          <Icon type="ios-arrow-dropdown" :color="index == openIndex? 'cyan':'white'" size="25" @click="toglleQuestionDetail(index)" :class="index == openIndex ? 'toggle-detail-icon toggle-detail-icon-up':'toggle-detail-icon toggle-detail-icon-down'" :title="questionDetail ? '收起':'查看详情'"/>
+        </p>
+        <div v-show="index == openIndex" :class="index == openIndex ? 'question-detail animated  fadeIn':'question-detail animated  fadeOut'">
+          <p class="option-item" v-for="(optionItem,index) in item.option">
+            <span class="option-order">{{optionItem.code+'.'}}</span>
+            <span class="option-text" v-html='optionItem.value'></span>
+          </p>
+          <p class="answer-label">答案:点击展开答案详情</p>
+          <p class="answer-detail">
+            <span v-for="(answerItem,index) in item.answer" v-html='answerItem'></span>
+          </p>
+        </div>
+      </div>-->
+    <QuestionList :questions="questionList"></QuestionList>
+    </vuescroll>
+  </div>
+</template>
+<script>
+  import QuestionList from  '@/components/learnactivity/QuestionList.vue'
+  export default {
+    components: {
+      QuestionList
+    },
+    props: {
+      questionList: {
+        type: Array,
+        default:[]
+      }
+    },
+    data() {
+      return {
+      }
+    },
+    methods: {
+      
+    },
+    mounted() {
+    }
+
+  }
+</script>
+<style lang="less">
+  @import "./TeacherPreview.less";
+</style>
+<style>
+  
+</style>

+ 184 - 0
TEAMModelOS/ClientApp/src/view/selflearning/CreateLearnUnit.less

@@ -0,0 +1,184 @@
+@main-bgColor: rgb(40,40,40); //主背景颜色
+@borderColor: #424242;
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+
+.learn-unit-container {
+  width: 100%;
+  height: 100%;
+
+  .learn-unit-header {
+    height: 45px;
+    line-height: 45px;
+    border-bottom: 1px solid @borderColor;
+    padding: 0px 20px;
+
+    .name-label {
+      color: @second-textColor;
+      font-size: @primary-fontSize;
+    }
+
+    .header-btn-save {
+      float: right;
+      margin-right: 15px;
+      color: #1CD0A1;
+      cursor:pointer;
+    }
+  }
+}
+.learn-unit-main {
+  width: 100%;
+  height: ~"calc(100% - 45px)";
+  display: flex;
+  flex-direction: row;
+
+  .choose-content-wrap {
+    padding-top: 15px;
+    padding-left: 20px;
+    width: 100%;
+    height: 100%;
+
+    .tab-wrap {
+      height: 100%;
+      background: #2c2c2c;
+      padding-top: 15px;
+      padding-bottom: 30px;
+
+      .content-filter-wrap {
+        width: 100%;
+        border-bottom: 1px solid #606060;
+        padding-left: 15px;
+        color: white;
+        position: relative;
+
+        .content-filter-item {
+          margin-bottom: 10px;
+        }
+
+        .content-filter-label {
+          color: white;
+          display: inline-block;
+          width: 90px;
+        }
+      }
+    }
+  }
+
+  .preview-content-wrap {
+    padding-left: 20px;
+    width: 100%;
+    height: 100%;
+
+    .preview-label {
+      color: @second-textColor;
+      font-size: 16px;
+      height: 45px;
+      line-height: 45px;
+      border-bottom: 1px solid @borderColor;
+    }
+
+    .content-type-label {
+      color: @second-textColor;
+      margin-top: 15px;
+    }
+
+    .content-file-wrap {
+      min-height: 300px;
+      color:white;
+    }
+
+    .content-question-wrap {
+      min-height: 200px;
+      
+    }
+  }
+}
+.choose-question-wrap {
+  height: 100%;
+  padding: 10px;
+  padding-bottom: 100px;
+}
+.radio-width {
+  width: 90px;
+}
+
+.seach-input{
+  position:absolute;
+  right:20px;
+  top:0px;
+}
+.syllabus-content-wrap {
+  display: flex;
+  flex-direction: row;
+  width: 100%;
+  height: 100%;
+
+  .volume-list-wrap {
+    padding-top: 10px;
+    padding-left: 15px;
+    width: 300px;
+    height: 100%;
+    border-right: 1px solid #606060;
+
+    .volume-list-label {
+      color: white;
+      font-size: 15px;
+      font-family: '微軟正黑體', 'Heiti TC';
+      border-bottom: 1px solid @borderColor;
+      padding-bottom: 10px;
+    }
+
+    .volume-item {
+      width: 100%;
+      border-bottom: 1px solid @borderColor;
+      padding: 15px 0px;
+      cursor: pointer;
+      color: white;
+
+      .volume-name {
+        font-size: 20px;
+      }
+
+      .volume-relation-num {
+        color: #0f9272;
+        font-size: 16px;
+        margin-top: 10px;
+      }
+    }
+
+    .volume-item-active {
+      background-image: linear-gradient(90deg, rgba(30, 30, 30, 0) 0%, rgba(110, 110, 110, 0.2) 50%, rgba(110, 110, 110, 0.4) 100%);
+    }
+  }
+
+  .syllabus-tree-wrap {
+    color: white;
+    padding-left: 15px;
+    padding-top: 10px;
+    width: 100%;
+
+    .syllabus-title-wrap {
+      color: white;
+      font-size: 15px;
+      font-family: '微軟正黑體', 'Heiti TC';
+      border-bottom: 1px solid @borderColor;
+      padding-bottom: 10px;
+      margin-bottom:1px;
+    }
+  }
+}
+.file-icon {
+  display: inline-block;
+
+  img {
+    display: inline-block;
+    margin-right: 10px;
+    vertical-align: text-top;
+    border-radius: 2px;
+  }
+}
+.file-content-wrap{
+  padding-top:5px;
+}

+ 562 - 0
TEAMModelOS/ClientApp/src/view/selflearning/CreateLearnUnit.vue

@@ -0,0 +1,562 @@
+<template>
+  <div class="learn-unit-container">
+    <div class="learn-unit-header">
+      <span class="name-label">名称:</span>
+      <Input v-model="learnUnit.name" placeholder="请输入名称..." style="width: 300px" />
+      <span class="header-btn-save" @click="saveData()">
+        <Icon type="ios-albums-outline" color="#1CD0A1" style="margin-right:5px;"/>
+        保存数据
+      </span>
+    </div>
+    <div class="learn-unit-main">
+      <Split v-model="split1">
+        <div slot="left" class="demo-split-pane">
+          <div class="choose-content-wrap">
+            <Tabs type="card">
+              <TabPane label="课纲">
+                <div class="tab-wrap">
+                  <div class="content-filter-wrap">
+                    <div class="content-filter-item">
+                      <span class="content-filter-label">课纲范围:</span>
+                      <RadioGroup v-model="syllabusFilter.type" style="display:inline-block;" @on-change="getVolumes">
+                        <Radio :label="1" class="radio-width">私有课纲</Radio>
+                        <Radio :label="0" class="radio-width">校本课纲</Radio>
+                      </RadioGroup>
+                    </div>
+                    <div class="content-filter-item">
+                      <span class="content-filter-label">学段:</span>
+                      <RadioGroup v-model="syllabusFilter.periodCode" style="display:inline-block;" @on-change="getSubjectList">
+                        <Radio class="radio-width" v-for="(item,index) in $store.state.schoolBaseInfo.schoolBaseInfo.period" :label="item.periodCode">{{item.periodName}}</Radio>
+                      </RadioGroup>
+                    </div>
+                    <div class="content-filter-item">
+                      <span class="content-filter-label">学科:</span>
+                      <RadioGroup v-model="syllabusFilter.subjectCode" style="display:inline-block;" @on-change="getVolumes">
+                        <Radio class="radio-width" v-for="(item,index) in subjectList" :label="item.subjectCode" :key="index">{{item.subjectName}}</Radio>
+                      </RadioGroup>
+                    </div>
+                  </div>
+                  <div class="syllabus-content-wrap">
+                    <div class="volume-list-wrap">
+                      <p class="volume-list-label">
+                        <Icon type="md-bookmarks" color="#fff" size="20" />
+                        <span style="margin-left:5px;">册别</span>
+                      </p>
+                      <div :class="index == currentVolumeIndex ? 'volume-item-active volume-item':'volume-item'" v-for="(item,index) in volumeList" :key="index" @click="selectVolume(index)">
+                        <p class="volume-name">{{item.volumeName}}</p>
+                        <div class="volume-relation-num">
+                          <Icon type="md-folder" title="内容资源数" style="margin-left:0" /> 8
+                          <Icon type="md-cube" title="关联知识点数" /> 10
+                          <Icon type="md-git-branch" title="节点数" /> 15
+                        </div>
+                      </div>
+                      <NoData style="margin-top:30px;" v-if="volumeList.length == 0"></NoData>
+                    </div>
+                    <div class="syllabus-tree-wrap">
+                      <!--<Tree  :data="syllabusList"></Tree>-->
+                      <div class="syllabus-title-wrap">
+                        <Icon type="ios-book" size="22" style="margin-right:5px;" />
+                        <span>{{volumeList.length > 0 ? volumeList[currentVolumeIndex].volumeName:'暂无数据'}}</span>
+                      </div>
+                      <el-tree v-if="syllabusList.length != 0" :data="syllabusList" :props="defaultProps" style="width:100%;" show-checkbox></el-tree>
+                      <NoData style="margin-top:30px;" v-if="syllabusList.length == 0"></NoData>
+                    </div>
+                  </div>
+                </div>
+              </TabPane>
+              <TabPane label="内容">
+                <div class="tab-wrap">
+                  <div class="content-filter-wrap">
+                    <div class="content-filter-item">
+                      <span class="content-filter-label">资源范围:</span>
+                      <RadioGroup v-model="syllabusFilter.type" style="display:inline-block;">
+                        <Radio label="0" class="radio-width">私有资源</Radio>
+                        <Radio label="1" class="radio-width">校本资源</Radio>
+                      </RadioGroup>
+                    </div>
+                    <div class="content-filter-item">
+                      <span class="content-filter-label">类型:</span>
+                      <RadioGroup v-model="syllabusFilter.periodCode" style="display:inline-block;" @on-change="getSubjectList">
+                        <Radio class="radio-width" v-for="(item,index) in contentTypeList" :label="item.type">{{item.label}}</Radio>
+                      </RadioGroup>
+                    </div>
+                    <Input class="seach-input" suffix="ios-search" placeholder="关键字搜索" clearable style="width: 240px" size="small"/>
+                  </div>
+                  <div class="file-content-wrap">
+                    <Table :columns="fileColumns" :data="fileListShow" class="animated fadeIn" @on-select="selectFile" @on-select-cancel="cancelSelectFile">
+                      <template slot-scope="{ row, index }" slot="size">
+                        <div>
+                          {{row.size/1204 > 1024 ? (row.size/1204/1204).toFixed(1) + 'M': (row.size/1204).toFixed(1) + 'KB'}}
+                        </div>
+                      </template>
+                      <template slot-scope="{ row, index }" slot="createTime">
+                        <div>
+                          {{formatDate(row.createTime)}}
+                        </div>
+                      </template>
+                      <template slot-scope="{ row, index }" slot="fileName">
+                        <div>
+                          <div class="file-icon">
+                            <img v-if="row.extension == 'ppt' || row.extension == 'pptx'" src="../../assets/icon/ppt50.png" width="15" />
+                            <img v-else-if="row.extension == 'doc' || row.extension == 'docx'" src="../../assets/icon/word50.png" width="15" />
+                            <img v-else-if="row.extension == 'xls' || row.extension == 'xlsx'" src="../../assets/icon/xls50.png" width="15" />
+                            <img v-else-if="row.extension == 'pdf'" src="../../assets/icon/pdf50.png" width="15" />
+                            <img v-else-if="row.type == 'picture'" src="../../assets/icon/icon_img.png" width="15" />
+                            <img v-else-if="row.type == 'video'" src="../../assets/icon/icon_video.png" width="15" />
+                            <img v-else src="../../assets/icon/prelearn50.png" width="15" />
+                          </div>
+                          <EditableLabel :ref="'fileNameLabel'+index" :content="row.fileName" @editComplete="handleEditFileName($event,index)">
+                          </EditableLabel>
+                          <!--<span style="margin-left:8px;vertical-align: text-bottom;">{{row.fileName}}</span>-->
+                        </div>
+                      </template>
+                      <template slot-scope="{ row, index }" slot="knowledges">
+
+                      </template>
+                      <template slot-scope="{ row,index }" slot="action">
+                        <div class="item-tools">
+                          <Icon type="md-download" size="18" color="white" :title="$t('teachContent.tips3')" @click="downloadFile(index)" />
+                          <Icon v-show="row.type === 'picture' || row.type === 'video' || row.extension == 'pdf'" type="md-eye" size="18" color="white" :title="$t('teachContent.tips4')" @click="openPreviewFile(index)" />
+                          <Icon type="md-infinite" size="18" color="white" :title="$t('teachContent.tips5')" @click="bindKnowledge(index)" />
+                          <Icon type="md-create" size="18" color="white" :title="$t('teachContent.tips6')" @click="setFileNameEdit(index)" />
+                          <Icon type="md-trash" size="18" color="white" :title="$t('teachContent.tips7')" @click="delFile(index)" />
+                        </div>
+                      </template>
+                    </Table>
+                  </div>
+                </div>
+              </TabPane>
+              <TabPane label="题目">
+                <div class="tab-wrap">
+                  <div class="content-filter-wrap">
+                    <div class="content-filter-item">
+                      <span class="content-filter-label">题目范围:</span>
+                      <RadioGroup v-model="syllabusFilter.type" style="display:inline-block;">
+                        <Radio :label="0" class="radio-width">私有题库</Radio>
+                        <Radio :label="1" class="radio-width">校本题库</Radio>
+                      </RadioGroup>
+                    </div>
+                    <div class="content-filter-item">
+                      <span class="content-filter-label">题型:</span>
+                      <RadioGroup v-model="syllabusFilter.periodCode" style="display:inline-block;" @on-change="getSubjectList">
+                        <Radio label="All" class="radio-width">全部</Radio>
+                        <Radio label="Single" class="radio-width">单选题</Radio>
+                        <Radio label="Multiple" class="radio-width">多选题</Radio>
+                        <Radio label="Judge" class="radio-width">判断题</Radio>
+                        <Radio label="Complete" class="radio-width">填空题</Radio>
+                        <Radio label="Subjective" class="radio-width">问答题</Radio>
+                        <Radio label="Compose" class="radio-width">综合题</Radio>
+                      </RadioGroup>
+                    </div>
+                    <Input class="seach-input" suffix="ios-search" placeholder="关键字搜索" clearable style="width: 240px" size="small" />
+                  </div>
+                  <div class="choose-question-wrap">
+                    <vuescroll v-if="questionList.length > 0">
+                      <QuestionList :showSelect="true" @seleteQuestion="seleteQuestion" :questions="questionList"></QuestionList>
+                    </vuescroll>
+                    <NoData style="margin-top:30px;" v-if="questionList.length == 0"></NoData>
+                  </div>
+                </div>
+              </TabPane>
+            </Tabs>
+          </div>
+          
+        </div>
+        <div slot="right" class="demo-split-pane">
+          <div class="preview-content-wrap">
+            <vuescroll>
+              <p class="preview-label">已选资源</p>
+              <p class="content-type-label">内容:{{learnUnit.files.length}}个</p>
+              <div class="content-file-wrap">
+                <NoData style="margin-top:30px;" v-if="learnUnit.files.length == 0"></NoData>
+                <p v-for="(item,index) in learnUnit.files">{{item.fileName}}</p>
+              </div>
+              <p class="content-type-label">题目:{{learnUnit.questions.length}}道</p>
+              <div class="content-question-wrap">
+                <NoData v-if="learnUnit.questions.length == 0" style="margin-top:30px;"></NoData>
+                <QuestionList v-else :questions="learnUnit.questions"></QuestionList>
+              </div>
+            </vuescroll>
+          </div>
+        </div>
+      </Split>
+    </div>
+  </div>
+</template>
+<script>
+  import questions from '../evaluation/index/list.json'
+  import EditableLabel from '@/common/EditableLabel.vue'
+  import NoData from '@/common/NoData.vue'
+  import JSONPath from 'jsonpath'
+  import QuestionList from  '@/components/learnactivity/QuestionList.vue'
+  export default {
+    data() {
+      return {
+        learnUnit: {
+          name:'',
+          files: [],
+          questions:[]
+        },
+        questionList: [],
+        searchBefore:[],
+        fileList:[],
+        fileListShow:[],
+        fileColumns: [],
+        defaultProps: {
+          children: 'children',
+          label: 'title'
+        },
+        volumeCode:'',
+        currentVolumeIndex:0,
+        syllabusList:[],
+        volumeList:[],
+        split1: 0.65,
+        syllabusFilter: {
+          type: 1,
+          periodCode: '',
+          subjectCode:'',
+          status: 1,
+          schoolCode:'HBCN'
+        },
+        subjectList: [],
+        contentTypeList:[
+          {
+            label:this.$t('teachContent.filterAll'),
+            type: 'all',
+            icon: 'md-apps'
+          },
+          {
+            label: this.$t('teachContent.filterPicture'),
+            type: 'picture',
+            icon: 'ios-image-outline'
+          },
+          {
+            label: this.$t('teachContent.filterVideo'),
+            type: 'video',
+            icon: 'logo-youtube'
+          },
+          {
+            label: this.$t('teachContent.filterDoc'),
+            type: 'document',
+            icon: 'logo-wordpress'
+          },
+          {
+            label: this.$t('teachContent.filterOther'),
+            type: 'other',
+            icon: 'md-filing'
+          }
+        ],
+        demoLoginInfo: {
+          user: 'admin',
+          TEAMModelId: 'habook#0001',
+          school: '醍摩豆书院',
+          schoolCode: 'HBCN'
+        },
+      }
+    },
+    components: {
+      NoData,
+      EditableLabel,
+      QuestionList
+    },
+    methods: {
+      seleteQuestion(question) {
+        let flag = true
+        for (let item of this.learnUnit.questions) {
+          if (item.shaCode == question.shaCode) {
+            flag = false
+            break
+          }
+        }
+        if (flag) {
+          this.learnUnit.questions.push(question)
+        } else {
+          this.$Message.warning('此题目已添加!')
+        }
+      },
+      cancelSelectFile(selection, row) {
+        for (let i = 0; i < this.learnUnit.files.length; i++) {
+          if (row.sha1Code == this.learnUnit.files[i].sha1Code) {
+            this.learnUnit.files.splice(i, 1)
+            break
+          }
+        }
+      },
+      selectFile(selection, row) {
+        this.learnUnit.files.push(row)
+      },
+      formatDate(timestamp) {
+        let date = new Date(timestamp)
+        return date.toLocaleString()
+      },
+      findFileList() {
+        this.$api.teachContent.findResourceByDict(
+          {
+            TEAMModelId: this.demoLoginInfo.TEAMModelId
+          }
+        ).then(
+          (res) => {
+            if (res.error == null) {
+              this.fileList = res.result.data
+              this.fileListShow = this.fileList
+              this.searchBefore = this.fileList
+              //this.storageSpace = this.fileList.reduce(
+              //  (total, item) => {
+              //    return total + item.size
+              //  }, 0
+              //)
+            } else {
+              this.$Message.warning(error.message)
+            }
+          },
+          (err) => {
+            alert('API error!')
+          }
+        )
+      },
+      initData() {
+        this.fileColumns = [
+          {
+            type: 'selection',
+            width: 60,
+            align: 'center'
+          },
+          {
+            title: this.$t('teachContent.tableC1'),
+            slot: 'fileName',
+            key: 'fileName',
+            sortable: true
+          },
+          //{
+          //  title: this.$t('teachContent.tableC2'),
+          //  slot: 'action',
+          //  width: 210,
+          //  align: 'center'
+          //},
+          //{
+          //  title: this.$t('teachContent.tableC3'),
+          //  slot: 'knowledges',
+          //  width: 180,
+          //  align: 'center'
+          //},
+          {
+            title: this.$t('teachContent.tableC4'),
+            slot: 'size',
+            key: 'size',
+            width: 110,
+            align: 'center',
+            sortable: true
+          },
+          {
+            title: this.$t('teachContent.tableC5'),
+            key: 'relationNum',
+            width: 160,
+            align: 'center',
+            sortable: true
+          },
+          {
+            title: this.$t('teachContent.tableC6'),
+            slot: 'createTime',
+            key: 'createTime',
+            width: 200,
+            sortable: true
+          }
+        ]
+      },
+      selectVolume(index) {
+        this.currentVolumeIndex = index
+        this.volumeCode = this.volumeList[this.currentVolumeIndex].volumeCode
+        this.getSyllabus()
+      },
+      getSyllabus() {
+        this.$api.syllabus.GetTreeByVolume({
+          volumeCode: this.volumeCode
+        }).then(
+          (res) => {
+            if (res.error == null) {
+              this.syllabusList = res.result.data
+            } else {
+              alert('API error!')
+            }
+          },
+          (err) => {
+            alert('API error!')
+          }
+        )
+      },
+      getVolumes() {
+        this.currentVolumeIndex = 0
+        this.$api.syllabus.GetVolumes(this.syllabusFilter).then(
+          (res) => {
+            if (res.error == null) {
+              this.volumeList = res.result.data
+              if (this.volumeList.length == 0) {
+                this.syllabusList.length = []
+              } else {
+                this.volumeCode = this.volumeList[this.currentVolumeIndex].volumeCode
+                this.getSyllabus()
+              }
+            } else {
+              alert('API error!')
+            }
+          },
+          (err) => {
+            alert('API error!')
+          }
+        )
+      },
+      getSubjectList() {
+        let result = JSONPath.query(this.$store.state.schoolBaseInfo.schoolBaseInfo.period, "$..[?(@.periodCode=='" + this.syllabusFilter.periodCode + "')]")
+        if (result.length > 0) {
+          this.subjectList = result[0].subjects
+          if (this.subjectList.length > 0) {
+            this.syllabusFilter.subjectCode = this.subjectList[0].subjectCode
+            this.getVolumes()
+          }
+        } else {
+          this.subjectList.length = 0
+          this.volumeList.length = 0
+          this.syllabusList.length = 0
+        }
+      },
+      getSchoolBaseInfo() {
+        this.$store.dispatch('schoolBaseInfo/getSchoolBaseData').then(
+          (res) => {
+            if (res.code == 2) {
+              alert('数据为空!')
+            } else {
+              if (this.$store.state.schoolBaseInfo.schoolBaseInfo.period.length > 0) {
+                this.syllabusFilter.periodCode = this.$store.state.schoolBaseInfo.schoolBaseInfo.period[0].periodCode
+                this.subjectList = this.$store.state.schoolBaseInfo.schoolBaseInfo.period[0].subjects
+                if (this.subjectList.length > 0) {
+                  this.syllabusFilter.subjectCode = this.subjectList[0].subjectCode
+                }
+              }
+            }
+            this.getVolumes()
+          },
+          (err) => {
+            alert('API error!')
+          }
+        )
+      },
+    },
+    created() {
+      this.initData()
+      this.getSchoolBaseInfo()
+      this.findFileList()
+      Date.prototype.toLocaleString = function () {
+        return this.getFullYear() + "/" + (this.getMonth() + 1) + "/" + this.getDate();
+      }
+      this.questionList = questions.result.data
+      console.log(this.questionList)
+    }
+  }
+</script>
+<style scoped lang="less">
+  @import "./CreateLearnUnit.less";
+</style>
+<style>
+  .learn-unit-header .ivu-input {
+    background: none;
+    color:white;
+    border-color:#424242;
+    font-size:16px;
+  }
+  .learn-unit-main .ivu-split-trigger-vertical {
+    width:3px;
+    background:#525252;
+  }
+  .learn-unit-main .ivu-split-trigger {
+    border-color:#424242;
+    border:none;
+  }
+  .learn-unit-main .ivu-split-trigger-bar-con.vertical {
+    left:0px;
+  }
+  .learn-unit-main .ivu-split-trigger-vertical .ivu-split-trigger-bar {
+    background:white;
+    width:3px;
+  }
+  .learn-unit-main .demo-split-pane {
+    height:100%;
+  }
+  .learn-unit-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-nav-container {
+    height: auto;
+  }
+  .learn-unit-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab {
+    padding-top: 3px;
+    height: auto;
+  }
+  .learn-unit-main .ivu-tabs-bar {
+    border-color: #404040;
+  }
+
+  .learn-unit-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab {
+    border: none;
+    background-color: #303030;
+    color: white;
+    margin-right: 2px;
+  }
+
+  .learn-unit-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab-active {
+    background-color: #2c2c2c;
+    color: white;
+    font-weight:600;
+  }
+  .learn-unit-main .ivu-tabs, .learn-unit-main .ivu-tabs .ivu-tabs-content-animated {
+    height:100%;
+  }
+  .learn-unit-main .ivu-tabs-bar {
+    margin-bottom:0px;
+  }
+  .tab-wrap .ivu-input {
+    background: none;
+    color: white;
+    border-color:#606060;
+    border-radius:15px;
+  }
+  .volume-relation-num .ivu-icon {
+    margin-left:15px;
+  }
+  .syllabus-tree-wrap .el-tree {
+    color: white;
+    background: none;
+    padding-top: 10px;
+    padding-bottom: 100px;
+  }
+  .syllabus-tree-wrap .el-tree-node__content {
+    height: 50px;
+  }
+
+    .syllabus-tree-wrap .el-tree-node__content:hover {
+      height: 50px;
+      background: #313131;
+    }
+
+  .syllabus-tree-wrap .el-tree-node:focus > .el-tree-node__content {
+    background: #313131;
+  }
+
+  .syllabus-tree-wrap .el-tree__empty-block {
+    display: none;
+  }
+
+  .file-content-wrap .ivu-table, .file-content-wrap .ivu-table td, .file-content-wrap .ivu-table th, .content-file-list .ivu-table:before {
+    background: none;
+    color: white;
+    border-color: #424242;
+  }
+
+  .file-content-wrap .ivu-table-row-hover .item-tools {
+    opacity: 1;
+    transition: opacity 1s;
+  }
+
+  .file-content-wrap .ivu-table-row-hover {
+    background: #303030;
+  }
+  .file-content-wrap .ivu-table:before {
+    height:0px;
+  }
+</style>

+ 55 - 0
TEAMModelOS/ClientApp/src/view/selflearning/CreateOrderLearn.less

@@ -0,0 +1,55 @@
+@main-bgColor: rgb(40,40,40); //主背景颜色
+@borderColor: #424242;
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+
+.template-wrap {
+  width: 100%;
+  height: 100%;
+  color:white;
+  .order-learn-header {
+    height: 45px;
+    line-height: 45px;
+    padding: 0px 15px;
+    border-bottom: 1px solid @borderColor;
+
+    .order-learn-title {
+      height: 45px;
+      line-height: 45px;
+      color: #fff;
+      font-size: 16px;
+      width: 150px;
+      display: inline-block;
+    }
+
+    .header-btn-save {
+      color: #6bdfc3;
+      float: right;
+      cursor: pointer;
+      line-height: 45px;
+      margin-right: 40px;
+    }
+  }
+
+  .order-learn-main {
+    width: 100%;
+    height: ~"calc(100% - 45px)";
+
+    .base-info-wrap {
+      width: 400px;
+      height: 100%;
+      border-right: 1px solid @borderColor;
+      padding-left: 15px;
+      
+      .base-info-label{
+        color:@second-textColor;
+        height :40px;
+        line-height:40px;
+        font-size:15px;
+        border-bottom:1px solid @borderColor; 
+      }
+    }
+  }
+}

+ 61 - 0
TEAMModelOS/ClientApp/src/view/selflearning/CreateOrderLearn.vue

@@ -0,0 +1,61 @@
+<template>
+  <div class="template-wrap">
+    <div class="order-learn-header">
+      <span class="order-learn-title">
+        创建编序式学习
+      </span>
+      <span class="header-btn-save" @click="saveData()">
+        <Icon type="ios-albums-outline" color="#1CD0A1" style="margin-right:5px;" />
+        保存数据
+      </span>
+    </div>
+    <div class="order-learn-main">
+      <div class="base-info-wrap">
+        <p class="base-info-label">基础信息</p>
+        <div style="width:100%; height:calc(100% - 45px);padding-top:30px;padding-right:15px;">
+          <!--<vuescroll>-->
+          <Form :model="orderLearnInfo" label-position="top" class="evaluation-attr-form" label-colon>
+            <FormItem label="评测名称">
+              <Input v-model="orderLearnInfo.name"></Input>
+            </FormItem>
+            <FormItem label="科目">
+              <Select v-model="orderLearnInfo.subject">
+                <Option v-for="(item,index) in typeList" :value="item" :key="index">{{ item }}</Option>
+              </Select>
+            </FormItem>
+            <FormItem label="学习对象">
+              <Select v-model="orderLearnInfo.target">
+                <Option v-for="(item,index) in classroomList" :value="item" :key="index">{{ item }}</Option>
+              </Select>
+            </FormItem>
+            <FormItem label="活动说明">
+              <Input v-model="orderLearnInfo.notice"></Input>
+            </FormItem>
+          </Form>
+          <!--</vuescroll>-->
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+  export default {
+    data() {
+      return {
+        orderLearnInfo: {
+          name: '',
+          subject: '',
+          target: [],
+          isPass: '',
+          notice:''
+        }
+      }
+    }
+  }
+</script>
+<style scoped lang="less">
+@import "./CreateOrderLearn.less";
+</style>
+<style>
+
+</style>

+ 28 - 15
TEAMModelOS/Controllers/Analysis/AchievementController.cs

@@ -1244,13 +1244,14 @@ namespace TEAMModelOS.Controllers.Analysis
             //说明 同学校、同类型考试数据
             List<ExamInfo> exams = await azureCosmosDBRepository.FindByDict<ExamInfo>(request.@params);
             HashSet<string> classList = new HashSet<string>();
+            List<List<List<string>>> AllPoint = new List<List<List<string>>>();
             exams.ForEach( async e => {
                 Dictionary<string, object> sub = new Dictionary<string, object>
                 {
                     { "ExamCode",  e.Id}
                 };
                 List<string> subjects = new List<string>();
-                Dictionary<string, List<string>> SubjectClassAverage = new Dictionary<string, List<string>>();
+                List<List<string>> SubjectClassAverage = new List<List<string>>();
                 //List<List<string>> SubjectClassAverage = new List<List<string>>();
                 List<SimpleExam> simples = await azureCosmosDBRepository.FindByDict<SimpleExam>(sub);
                 simples.ForEach(s =>
@@ -1260,11 +1261,9 @@ namespace TEAMModelOS.Controllers.Analysis
                         { "name", s.Subject }
                     };
                     subjects.Add(s.Subject);
+                    //计算试卷总分
+                    int sum = s.Point.Sum();
                     List<string> ClassAverage = new List<string>();
-                    //初始化科目总分
-                    double subjectPoint = 0;
-                    //科目平均分
-                    double subAverage = 0;
                     List<string> passList = new List<string>();
                     s.Classes.ForEach(c =>
                     {
@@ -1297,21 +1296,35 @@ namespace TEAMModelOS.Controllers.Analysis
 
                             //每个班级实际参考人数
                             counts = value[1] - value[0] - classCount + 1;
-                            points = Convert.ToDouble(classPoint) / counts;
+                            points = Convert.ToDouble(classPoint) / counts / sum;
                             ClassAverage.Add(points.ToString("0.00"));                          
                         }
-                    });
-                    SubjectClassAverage.Add(s.Subject, ClassAverage);
-                    //科目平均分
-                    ClassAverage.ForEach(a =>
-                    {
-                        subjectPoint += Convert.ToDouble(a);
-                    });
-                    subAverage = subjectPoint / ClassAverage.Count();
+                    });                  
+                    SubjectClassAverage.Add(ClassAverage);                    
+                    //subAverage = subjectPoint / ClassAverage.Count();
                     //subjectAverage.Add(subAverage.ToString("0.00"));
                 });
+                AllPoint.Add(SubjectClassAverage);
+                            
             });
-            //builder.Data(info);
+            List<List<double>> ww = new List<List<double>>();
+            
+            int k = AllPoint.Count;
+            for (int m = 0; m < AllPoint[0].Count; m++)
+            {
+                List<double> qq = new List<double>();
+                for (int n = 0; n < AllPoint[0][m].Count; n++)
+                {
+                    double startPoint = 0;
+                    for (int f = 0; f < k;f++) {
+                        startPoint = double.Parse(AllPoint[f][m][n]) + startPoint;
+                    }
+                    qq.Add(startPoint);
+                }
+                ww.Add(qq);
+            }
+            //AllPoint[0][0][0] + AllPoint[1][0][0];
+            builder.Data(ww);
             return builder.build();
         }
     }